Skip to content

Commit a08cf84

Browse files
committed
Move forceErrorForFiberIDs/forceFallbackForSuspenseIDs to use flags on the instance
Get more use out of the instance.
1 parent 9a80a3d commit a08cf84

File tree

1 file changed

+84
-64
lines changed
  • packages/react-devtools-shared/src/backend/fiber

1 file changed

+84
-64
lines changed

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

+84-64
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,9 @@ export function attach(
893902
args: $ReadOnlyArray<any>,
894903
): void {
895904
if (type === 'error') {
896-
const maybeID = getFiberIDUnsafe(fiber);
905+
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
897906
// if this is an error simulated by us to trigger error boundary, ignore
898-
if (maybeID != null && forceErrorForFiberIDs.get(maybeID) === true) {
907+
if (fiberInstance !== undefined && fiberInstance.flags & FORCE_ERROR) {
899908
return;
900909
}
901910
}
@@ -1343,13 +1352,20 @@ export function attach(
13431352
}
13441353

13451354
untrackFibersSet.forEach(fiber => {
1346-
const fiberID = getFiberIDUnsafe(fiber);
1347-
if (fiberID !== null) {
1348-
idToDevToolsInstanceMap.delete(fiberID);
1355+
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
1356+
if (fiberInstance !== undefined) {
1357+
idToDevToolsInstanceMap.delete(fiberInstance.id);
13491358

13501359
// Also clear any errors/warnings associated with this fiber.
1351-
clearErrorsForElementID(fiberID);
1352-
clearWarningsForElementID(fiberID);
1360+
clearErrorsForElementID(fiberInstance.id);
1361+
clearWarningsForElementID(fiberInstance.id);
1362+
if (fiberInstance.flags & FORCE_ERROR) {
1363+
fiberInstance.flags &= ~FORCE_ERROR;
1364+
forceErrorCount--;
1365+
if (forceErrorCount === 0 && setErrorHandler != null) {
1366+
setErrorHandler(shouldErrorFiberAlwaysNull);
1367+
}
1368+
}
13531369
}
13541370

13551371
fiberToFiberInstanceMap.delete(fiber);
@@ -1358,13 +1374,6 @@ export function attach(
13581374
if (alternate !== null) {
13591375
fiberToFiberInstanceMap.delete(alternate);
13601376
}
1361-
1362-
if (forceErrorForFiberIDs.has(fiberID)) {
1363-
forceErrorForFiberIDs.delete(fiberID);
1364-
if (forceErrorForFiberIDs.size === 0 && setErrorHandler != null) {
1365-
setErrorHandler(shouldErrorFiberAlwaysNull);
1366-
}
1367-
}
13681377
});
13691378
untrackFibersSet.clear();
13701379
}
@@ -3504,7 +3513,7 @@ export function attach(
35043513
const DidCapture = 0b000000000000000000010000000;
35053514
isErrored =
35063515
(fiber.flags & DidCapture) !== 0 ||
3507-
forceErrorForFiberIDs.get(id) === true;
3516+
(devtoolsInstance.flags & FORCE_ERROR) !== 0;
35083517
targetErrorBoundaryID = isErrored ? id : getNearestErrorBoundaryID(fiber);
35093518
} else {
35103519
targetErrorBoundaryID = getNearestErrorBoundaryID(fiber);
@@ -3553,7 +3562,7 @@ export function attach(
35533562
(!isTimedOutSuspense ||
35543563
// If it's showing fallback because we previously forced it to,
35553564
// allow toggling it back to remove the fallback override.
3556-
forceFallbackForSuspenseIDs.has(id)),
3565+
(devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0),
35573566

35583567
// Can view component source location.
35593568
canViewSource,
@@ -4352,44 +4361,42 @@ export function attach(
43524361
return null;
43534362
}
43544363

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>();
4364+
let forceErrorCount = 0;
43584365

4359-
function shouldErrorFiberAccordingToMap(fiber: any) {
4366+
function shouldErrorFiberAccordingToMap(fiber: any): null | boolean {
43604367
if (typeof setErrorHandler !== 'function') {
43614368
throw new Error(
43624369
'Expected overrideError() to not get called for earlier React versions.',
43634370
);
43644371
}
43654372

4366-
const id = getFiberIDUnsafe(fiber);
4367-
if (id === null) {
4373+
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
4374+
if (fiberInstance === undefined) {
43684375
return null;
43694376
}
43704377

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-
}
4378+
if (fiberInstance.flags & FORCE_ERROR_RESET) {
4379+
// TRICKY overrideError adds entries to this Map,
4380+
// so ideally it would be the method that clears them too,
4381+
// but that would break the functionality of the feature,
4382+
// since DevTools needs to tell React to act differently than it normally would
4383+
// (don't just re-render the failed boundary, but reset its errored state too).
4384+
// So we can only clear it after telling React to reset the state.
4385+
// Technically this is premature and we should schedule it for later,
4386+
// since the render could always fail without committing the updated error boundary,
4387+
// but since this is a DEV-only feature, the simplicity is worth the trade off.
4388+
forceErrorCount--;
4389+
fiberInstance.flags &= ~FORCE_ERROR_RESET;
4390+
if (forceErrorCount === 0) {
4391+
// Last override is gone. Switch React back to fast path.
4392+
setErrorHandler(shouldErrorFiberAlwaysNull);
43904393
}
4394+
return false;
4395+
} else if (fiberInstance.flags & FORCE_ERROR) {
4396+
return true;
4397+
} else {
4398+
return null;
43914399
}
4392-
return status;
43934400
}
43944401

43954402
function overrideError(id: number, forceError: boolean) {
@@ -4402,17 +4409,20 @@ export function attach(
44024409
);
44034410
}
44044411

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-
44124412
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
44134413
if (devtoolsInstance === undefined) {
44144414
return;
44154415
}
4416+
if ((devtoolsInstance.flags & (FORCE_ERROR | FORCE_ERROR_RESET)) === 0) {
4417+
forceErrorCount++;
4418+
if (forceErrorCount === 1) {
4419+
// First override is added. Switch React to slower path.
4420+
setErrorHandler(shouldErrorFiberAccordingToMap);
4421+
}
4422+
}
4423+
devtoolsInstance.flags &= forceError ? ~FORCE_ERROR_RESET : ~FORCE_ERROR;
4424+
devtoolsInstance.flags |= forceError ? FORCE_ERROR : FORCE_ERROR_RESET;
4425+
44164426
if (devtoolsInstance.kind === FIBER_INSTANCE) {
44174427
const fiber = devtoolsInstance.data;
44184428
scheduleUpdate(fiber);
@@ -4425,11 +4435,14 @@ export function attach(
44254435
return false;
44264436
}
44274437

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

44304440
function shouldSuspendFiberAccordingToSet(fiber: any) {
4431-
const maybeID = getFiberIDUnsafe(((fiber: any): Fiber));
4432-
return maybeID !== null && forceFallbackForSuspenseIDs.has(maybeID);
4441+
const fiberInstance = fiberToFiberInstanceMap.get(fiber);
4442+
return (
4443+
fiberInstance !== undefined &&
4444+
(fiberInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0
4445+
);
44334446
}
44344447

44354448
function overrideSuspense(id: number, forceFallback: boolean) {
@@ -4441,24 +4454,31 @@ export function attach(
44414454
'Expected overrideSuspense() to not get called for earlier React versions.',
44424455
);
44434456
}
4457+
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
4458+
if (devtoolsInstance === undefined) {
4459+
return;
4460+
}
4461+
44444462
if (forceFallback) {
4445-
forceFallbackForSuspenseIDs.add(id);
4446-
if (forceFallbackForSuspenseIDs.size === 1) {
4447-
// First override is added. Switch React to slower path.
4448-
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
4463+
if ((devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) === 0) {
4464+
devtoolsInstance.flags |= FORCE_SUSPENSE_FALLBACK;
4465+
forceFallbackCount++;
4466+
if (forceFallbackCount === 1) {
4467+
// First override is added. Switch React to slower path.
4468+
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
4469+
}
44494470
}
44504471
} else {
4451-
forceFallbackForSuspenseIDs.delete(id);
4452-
if (forceFallbackForSuspenseIDs.size === 0) {
4453-
// Last override is gone. Switch React back to fast path.
4454-
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
4472+
if ((devtoolsInstance.flags & FORCE_SUSPENSE_FALLBACK) !== 0) {
4473+
devtoolsInstance.flags &= ~FORCE_SUSPENSE_FALLBACK;
4474+
forceFallbackCount--;
4475+
if (forceFallbackCount === 0) {
4476+
// Last override is gone. Switch React back to fast path.
4477+
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
4478+
}
44554479
}
44564480
}
44574481

4458-
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
4459-
if (devtoolsInstance === undefined) {
4460-
return;
4461-
}
44624482
if (devtoolsInstance.kind === FIBER_INSTANCE) {
44634483
const fiber = devtoolsInstance.data;
44644484
scheduleUpdate(fiber);

0 commit comments

Comments
 (0)