Skip to content

Commit 1f3892e

Browse files
committed
Move forceErrorForFiberIDs/forceFallbackForSuspenseIDs to use flags on the instance
Get more use out of the instance.
1 parent 06eda90 commit 1f3892e

File tree

1 file changed

+104
-65
lines changed
  • packages/react-devtools-shared/src/backend/fiber

1 file changed

+104
-65
lines changed

packages/react-devtools-shared/src/backend/fiber/renderer.js

+104-65
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,22 @@ import type {
133133
import type {Source} from 'react-devtools-shared/src/shared/types';
134134
import {getStackByFiberInDevAndProd} from './DevToolsFiberComponentStack';
135135

136+
// Kinds
136137
const FIBER_INSTANCE = 0;
137138
// const VIRTUAL_INSTANCE = 1;
138139

140+
// Flags
141+
const FORCE_SUSPENSE_FALLBACK = /* */ 0b001;
142+
const FORCE_ERROR = /* */ 0b010;
143+
const FORCE_ERROR_RESET = /* */ 0b100;
144+
139145
// This type represents a stateful instance of a Client Component i.e. a Fiber pair.
140146
// These instances also let us track stateful DevTools meta data like id and warnings.
141147
type FiberInstance = {
142148
kind: 0,
143149
id: number,
144150
parent: null | DevToolsInstance, // virtual parent
151+
flags: number, // Force Error/Suspense
145152
componentStack: null | string,
146153
errors: null | Map<string, number>, // error messages and count
147154
warnings: null | Map<string, number>, // warning messages and count
@@ -153,6 +160,7 @@ function createFiberInstance(fiber: Fiber): FiberInstance {
153160
kind: 0,
154161
id: getUID(),
155162
parent: null,
163+
flags: 0,
156164
componentStack: null,
157165
errors: null,
158166
warnings: null,
@@ -169,6 +177,7 @@ type VirtualInstance = {
169177
kind: 1,
170178
id: number,
171179
parent: null | DevToolsInstance, // virtual parent
180+
flags: number,
172181
componentStack: null | string,
173182
// Errors and Warnings happen per ReactComponentInfo which can appear in
174183
// multiple places but we track them per stateful VirtualInstance so
@@ -893,9 +902,12 @@ export function attach(
893902
args: $ReadOnlyArray<any>,
894903
): void {
895904
if (type === 'error') {
896-
const maybeID = getFiberIDUnsafe(fiber);
905+
let fiberInstance = fiberToFiberInstanceMap.get(fiber);
906+
if (fiberInstance === undefined && fiber.alternate !== null) {
907+
fiberInstance = fiberToFiberInstanceMap.get(fiber.alternate);
908+
}
897909
// if this is an error simulated by us to trigger error boundary, ignore
898-
if (maybeID != null && forceErrorForFiberIDs.get(maybeID) === true) {
910+
if (fiberInstance !== undefined && fiberInstance.flags & FORCE_ERROR) {
899911
return;
900912
}
901913
}
@@ -1343,13 +1355,27 @@ export function attach(
13431355
}
13441356

13451357
untrackFibersSet.forEach(fiber => {
1346-
const fiberID = getFiberIDUnsafe(fiber);
1347-
if (fiberID !== null) {
1348-
idToDevToolsInstanceMap.delete(fiberID);
1358+
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
1359+
if (fiberInstance !== undefined) {
1360+
idToDevToolsInstanceMap.delete(fiberInstance.id);
13491361

13501362
// Also clear any errors/warnings associated with this fiber.
1351-
clearErrorsForElementID(fiberID);
1352-
clearWarningsForElementID(fiberID);
1363+
clearErrorsForElementID(fiberInstance.id);
1364+
clearWarningsForElementID(fiberInstance.id);
1365+
if (fiberInstance.flags & FORCE_ERROR) {
1366+
fiberInstance.flags &= ~FORCE_ERROR;
1367+
forceErrorCount--;
1368+
if (forceErrorCount === 0 && setErrorHandler != null) {
1369+
setErrorHandler(shouldErrorFiberAlwaysNull);
1370+
}
1371+
}
1372+
if (fiberInstance.flags & FORCE_SUSPENSE_FALLBACK) {
1373+
fiberInstance.flags &= ~FORCE_SUSPENSE_FALLBACK;
1374+
forceFallbackCount--;
1375+
if (forceFallbackCount === 0 && setSuspenseHandler != null) {
1376+
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
1377+
}
1378+
}
13531379
}
13541380

13551381
fiberToFiberInstanceMap.delete(fiber);
@@ -1358,13 +1384,6 @@ export function attach(
13581384
if (alternate !== null) {
13591385
fiberToFiberInstanceMap.delete(alternate);
13601386
}
1361-
1362-
if (forceErrorForFiberIDs.has(fiberID)) {
1363-
forceErrorForFiberIDs.delete(fiberID);
1364-
if (forceErrorForFiberIDs.size === 0 && setErrorHandler != null) {
1365-
setErrorHandler(shouldErrorFiberAlwaysNull);
1366-
}
1367-
}
13681387
});
13691388
untrackFibersSet.clear();
13701389
}
@@ -3504,7 +3523,7 @@ export function attach(
35043523
const DidCapture = 0b000000000000000000010000000;
35053524
isErrored =
35063525
(fiber.flags & DidCapture) !== 0 ||
3507-
forceErrorForFiberIDs.get(id) === true;
3526+
(devtoolsInstance.flags & FORCE_ERROR) !== 0;
35083527
targetErrorBoundaryID = isErrored ? id : getNearestErrorBoundaryID(fiber);
35093528
} else {
35103529
targetErrorBoundaryID = getNearestErrorBoundaryID(fiber);
@@ -3553,7 +3572,7 @@ export function attach(
35533572
(!isTimedOutSuspense ||
35543573
// If it's showing fallback because we previously forced it to,
35553574
// allow toggling it back to remove the fallback override.
3556-
forceFallbackForSuspenseIDs.has(id)),
3575+
(devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0),
35573576

35583577
// Can view component source location.
35593578
canViewSource,
@@ -4352,44 +4371,45 @@ export function attach(
43524371
return null;
43534372
}
43544373

4355-
// Map of id and its force error status: true (error), false (toggled off),
4356-
// null (do nothing)
4357-
const forceErrorForFiberIDs = new Map<number | null, $FlowFixMe>();
4374+
let forceErrorCount = 0;
43584375

4359-
function shouldErrorFiberAccordingToMap(fiber: any) {
4376+
function shouldErrorFiberAccordingToMap(fiber: any): null | boolean {
43604377
if (typeof setErrorHandler !== 'function') {
43614378
throw new Error(
43624379
'Expected overrideError() to not get called for earlier React versions.',
43634380
);
43644381
}
43654382

4366-
const id = getFiberIDUnsafe(fiber);
4367-
if (id === null) {
4383+
let fiberInstance = fiberToFiberInstanceMap.get(fiber);
4384+
if (fiberInstance === undefined && fiber.alternate !== null) {
4385+
fiberInstance = fiberToFiberInstanceMap.get(fiber.alternate);
4386+
}
4387+
if (fiberInstance === undefined) {
43684388
return null;
43694389
}
43704390

4371-
let status = null;
4372-
if (forceErrorForFiberIDs.has(id)) {
4373-
status = forceErrorForFiberIDs.get(id);
4374-
if (status === false) {
4375-
// TRICKY overrideError adds entries to this Map,
4376-
// so ideally it would be the method that clears them too,
4377-
// but that would break the functionality of the feature,
4378-
// since DevTools needs to tell React to act differently than it normally would
4379-
// (don't just re-render the failed boundary, but reset its errored state too).
4380-
// So we can only clear it after telling React to reset the state.
4381-
// Technically this is premature and we should schedule it for later,
4382-
// since the render could always fail without committing the updated error boundary,
4383-
// but since this is a DEV-only feature, the simplicity is worth the trade off.
4384-
forceErrorForFiberIDs.delete(id);
4385-
4386-
if (forceErrorForFiberIDs.size === 0) {
4387-
// Last override is gone. Switch React back to fast path.
4388-
setErrorHandler(shouldErrorFiberAlwaysNull);
4389-
}
4391+
if (fiberInstance.flags & FORCE_ERROR_RESET) {
4392+
// TRICKY overrideError adds entries to this Map,
4393+
// so ideally it would be the method that clears them too,
4394+
// but that would break the functionality of the feature,
4395+
// since DevTools needs to tell React to act differently than it normally would
4396+
// (don't just re-render the failed boundary, but reset its errored state too).
4397+
// So we can only clear it after telling React to reset the state.
4398+
// Technically this is premature and we should schedule it for later,
4399+
// since the render could always fail without committing the updated error boundary,
4400+
// but since this is a DEV-only feature, the simplicity is worth the trade off.
4401+
forceErrorCount--;
4402+
fiberInstance.flags &= ~FORCE_ERROR_RESET;
4403+
if (forceErrorCount === 0) {
4404+
// Last override is gone. Switch React back to fast path.
4405+
setErrorHandler(shouldErrorFiberAlwaysNull);
43904406
}
4407+
return false;
4408+
} else if (fiberInstance.flags & FORCE_ERROR) {
4409+
return true;
4410+
} else {
4411+
return null;
43914412
}
4392-
return status;
43934413
}
43944414

43954415
function overrideError(id: number, forceError: boolean) {
@@ -4402,17 +4422,20 @@ export function attach(
44024422
);
44034423
}
44044424

4405-
forceErrorForFiberIDs.set(id, forceError);
4406-
4407-
if (forceErrorForFiberIDs.size === 1) {
4408-
// First override is added. Switch React to slower path.
4409-
setErrorHandler(shouldErrorFiberAccordingToMap);
4410-
}
4411-
44124425
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
44134426
if (devtoolsInstance === undefined) {
44144427
return;
44154428
}
4429+
if ((devtoolsInstance.flags & (FORCE_ERROR | FORCE_ERROR_RESET)) === 0) {
4430+
forceErrorCount++;
4431+
if (forceErrorCount === 1) {
4432+
// First override is added. Switch React to slower path.
4433+
setErrorHandler(shouldErrorFiberAccordingToMap);
4434+
}
4435+
}
4436+
devtoolsInstance.flags &= forceError ? ~FORCE_ERROR_RESET : ~FORCE_ERROR;
4437+
devtoolsInstance.flags |= forceError ? FORCE_ERROR : FORCE_ERROR_RESET;
4438+
44164439
if (devtoolsInstance.kind === FIBER_INSTANCE) {
44174440
const fiber = devtoolsInstance.data;
44184441
scheduleUpdate(fiber);
@@ -4425,11 +4448,17 @@ export function attach(
44254448
return false;
44264449
}
44274450

4428-
const forceFallbackForSuspenseIDs = new Set<number>();
4451+
let forceFallbackCount = 0;
44294452

44304453
function shouldSuspendFiberAccordingToSet(fiber: any) {
4431-
const maybeID = getFiberIDUnsafe(((fiber: any): Fiber));
4432-
return maybeID !== null && forceFallbackForSuspenseIDs.has(maybeID);
4454+
let fiberInstance = fiberToFiberInstanceMap.get(fiber);
4455+
if (fiberInstance === undefined && fiber.alternate !== null) {
4456+
fiberInstance = fiberToFiberInstanceMap.get(fiber.alternate);
4457+
}
4458+
return (
4459+
fiberInstance !== undefined &&
4460+
(fiberInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0
4461+
);
44334462
}
44344463

44354464
function overrideSuspense(id: number, forceFallback: boolean) {
@@ -4441,24 +4470,31 @@ export function attach(
44414470
'Expected overrideSuspense() to not get called for earlier React versions.',
44424471
);
44434472
}
4473+
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
4474+
if (devtoolsInstance === undefined) {
4475+
return;
4476+
}
4477+
44444478
if (forceFallback) {
4445-
forceFallbackForSuspenseIDs.add(id);
4446-
if (forceFallbackForSuspenseIDs.size === 1) {
4447-
// First override is added. Switch React to slower path.
4448-
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
4479+
if ((devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) === 0) {
4480+
devtoolsInstance.flags |= FORCE_SUSPENSE_FALLBACK;
4481+
forceFallbackCount++;
4482+
if (forceFallbackCount === 1) {
4483+
// First override is added. Switch React to slower path.
4484+
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
4485+
}
44494486
}
44504487
} else {
4451-
forceFallbackForSuspenseIDs.delete(id);
4452-
if (forceFallbackForSuspenseIDs.size === 0) {
4453-
// Last override is gone. Switch React back to fast path.
4454-
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
4488+
if ((devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0) {
4489+
devtoolsInstance.flags &= ~FORCE_SUSPENSE_FALLBACK;
4490+
forceFallbackCount--;
4491+
if (forceFallbackCount === 0) {
4492+
// Last override is gone. Switch React back to fast path.
4493+
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
4494+
}
44554495
}
44564496
}
44574497

4458-
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
4459-
if (devtoolsInstance === undefined) {
4460-
return;
4461-
}
44624498
if (devtoolsInstance.kind === FIBER_INSTANCE) {
44634499
const fiber = devtoolsInstance.data;
44644500
scheduleUpdate(fiber);
@@ -4718,7 +4754,10 @@ export function attach(
47184754

47194755
function getComponentStackForFiber(fiber: Fiber): string | null {
47204756
// TODO: This should really just take an DevToolsInstance directly.
4721-
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
4757+
let fiberInstance = fiberToFiberInstanceMap.get(fiber);
4758+
if (fiberInstance === undefined && fiber.alternate !== null) {
4759+
fiberInstance = fiberToFiberInstanceMap.get(fiber.alternate);
4760+
}
47224761
if (fiberInstance === undefined) {
47234762
// We're no longer tracking this instance.
47244763
return null;

0 commit comments

Comments
 (0)