Skip to content

Commit 4a2dc76

Browse files
Brian Vaughnkoto
Brian Vaughn
authored andcommitted
Re-add "strict effects mode" for legacy roots only (facebook#20639)
This combines changes originally made in facebook#19523, facebook#20028, and facebook#20415 but with slightly different semantics: "strict effects" mode is enabled only for the experimental root APIs (never for legacy render, regardless of <StrictMode> usage). These semantics may change slightly in the future.
1 parent 0b22b27 commit 4a2dc76

10 files changed

+707
-102
lines changed

packages/react-reconciler/src/ReactFiberClassComponent.new.js

+49-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import type {Lanes} from './ReactFiberLane.new';
1212
import type {UpdateQueue} from './ReactUpdateQueue.new';
1313

1414
import * as React from 'react';
15-
import {Update, Snapshot} from './ReactFiberFlags';
15+
import {MountLayoutDev, Update, Snapshot} from './ReactFiberFlags';
1616
import {
1717
debugRenderPhaseSideEffectsForStrictMode,
1818
disableLegacyContext,
1919
enableDebugTracing,
2020
enableSchedulingProfiler,
2121
warnAboutDeprecatedLifecycles,
22+
enableDoubleInvokingEffects,
2223
} from 'shared/ReactFeatureFlags';
2324
import ReactStrictModeWarnings from './ReactStrictModeWarnings.new';
2425
import {isMounted} from './ReactFiberTreeReflection';
@@ -29,7 +30,13 @@ import invariant from 'shared/invariant';
2930
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
3031

3132
import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
32-
import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
33+
import {
34+
BlockingMode,
35+
ConcurrentMode,
36+
DebugTracingMode,
37+
NoMode,
38+
StrictMode,
39+
} from './ReactTypeOfMode';
3340

3441
import {
3542
enqueueUpdate,
@@ -890,7 +897,16 @@ function mountClassInstance(
890897
}
891898

892899
if (typeof instance.componentDidMount === 'function') {
893-
workInProgress.flags |= Update;
900+
if (
901+
__DEV__ &&
902+
enableDoubleInvokingEffects &&
903+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
904+
) {
905+
// Never double-invoke effects for legacy roots.
906+
workInProgress.flags |= MountLayoutDev | Update;
907+
} else {
908+
workInProgress.flags |= Update;
909+
}
894910
}
895911
}
896912

@@ -960,7 +976,16 @@ function resumeMountClassInstance(
960976
// If an update was already in progress, we should schedule an Update
961977
// effect even though we're bailing out, so that cWU/cDU are called.
962978
if (typeof instance.componentDidMount === 'function') {
963-
workInProgress.flags |= Update;
979+
if (
980+
__DEV__ &&
981+
enableDoubleInvokingEffects &&
982+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
983+
) {
984+
// Never double-invoke effects for legacy roots.
985+
workInProgress.flags |= MountLayoutDev | Update;
986+
} else {
987+
workInProgress.flags |= Update;
988+
}
964989
}
965990
return false;
966991
}
@@ -1003,13 +1028,31 @@ function resumeMountClassInstance(
10031028
}
10041029
}
10051030
if (typeof instance.componentDidMount === 'function') {
1006-
workInProgress.flags |= Update;
1031+
if (
1032+
__DEV__ &&
1033+
enableDoubleInvokingEffects &&
1034+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
1035+
) {
1036+
// Never double-invoke effects for legacy roots.
1037+
workInProgress.flags |= MountLayoutDev | Update;
1038+
} else {
1039+
workInProgress.flags |= Update;
1040+
}
10071041
}
10081042
} else {
10091043
// If an update was already in progress, we should schedule an Update
10101044
// effect even though we're bailing out, so that cWU/cDU are called.
10111045
if (typeof instance.componentDidMount === 'function') {
1012-
workInProgress.flags |= Update;
1046+
if (
1047+
__DEV__ &&
1048+
enableDoubleInvokingEffects &&
1049+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
1050+
) {
1051+
// Never double-invoke effects for legacy roots.
1052+
workInProgress.flags |= MountLayoutDev | Update;
1053+
} else {
1054+
workInProgress.flags |= Update;
1055+
}
10131056
}
10141057

10151058
// If shouldComponentUpdate returned false, we should still update the

packages/react-reconciler/src/ReactFiberClassComponent.old.js

+49-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import type {Lanes} from './ReactFiberLane.old';
1212
import type {UpdateQueue} from './ReactUpdateQueue.old';
1313

1414
import * as React from 'react';
15-
import {Update, Snapshot} from './ReactFiberFlags';
15+
import {MountLayoutDev, Update, Snapshot} from './ReactFiberFlags';
1616
import {
1717
debugRenderPhaseSideEffectsForStrictMode,
1818
disableLegacyContext,
1919
enableDebugTracing,
2020
enableSchedulingProfiler,
2121
warnAboutDeprecatedLifecycles,
22+
enableDoubleInvokingEffects,
2223
} from 'shared/ReactFeatureFlags';
2324
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
2425
import {isMounted} from './ReactFiberTreeReflection';
@@ -29,7 +30,13 @@ import invariant from 'shared/invariant';
2930
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
3031

3132
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
32-
import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
33+
import {
34+
BlockingMode,
35+
ConcurrentMode,
36+
DebugTracingMode,
37+
NoMode,
38+
StrictMode,
39+
} from './ReactTypeOfMode';
3340

3441
import {
3542
enqueueUpdate,
@@ -890,7 +897,16 @@ function mountClassInstance(
890897
}
891898

892899
if (typeof instance.componentDidMount === 'function') {
893-
workInProgress.flags |= Update;
900+
if (
901+
__DEV__ &&
902+
enableDoubleInvokingEffects &&
903+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
904+
) {
905+
// Never double-invoke effects for legacy roots.
906+
workInProgress.flags |= MountLayoutDev | Update;
907+
} else {
908+
workInProgress.flags |= Update;
909+
}
894910
}
895911
}
896912

@@ -960,7 +976,16 @@ function resumeMountClassInstance(
960976
// If an update was already in progress, we should schedule an Update
961977
// effect even though we're bailing out, so that cWU/cDU are called.
962978
if (typeof instance.componentDidMount === 'function') {
963-
workInProgress.flags |= Update;
979+
if (
980+
__DEV__ &&
981+
enableDoubleInvokingEffects &&
982+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
983+
) {
984+
// Never double-invoke effects for legacy roots.
985+
workInProgress.flags |= MountLayoutDev | Update;
986+
} else {
987+
workInProgress.flags |= Update;
988+
}
964989
}
965990
return false;
966991
}
@@ -1003,13 +1028,31 @@ function resumeMountClassInstance(
10031028
}
10041029
}
10051030
if (typeof instance.componentDidMount === 'function') {
1006-
workInProgress.flags |= Update;
1031+
if (
1032+
__DEV__ &&
1033+
enableDoubleInvokingEffects &&
1034+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
1035+
) {
1036+
// Never double-invoke effects for legacy roots.
1037+
workInProgress.flags |= MountLayoutDev | Update;
1038+
} else {
1039+
workInProgress.flags |= Update;
1040+
}
10071041
}
10081042
} else {
10091043
// If an update was already in progress, we should schedule an Update
10101044
// effect even though we're bailing out, so that cWU/cDU are called.
10111045
if (typeof instance.componentDidMount === 'function') {
1012-
workInProgress.flags |= Update;
1046+
if (
1047+
__DEV__ &&
1048+
enableDoubleInvokingEffects &&
1049+
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
1050+
) {
1051+
// Never double-invoke effects for legacy roots.
1052+
workInProgress.flags |= MountLayoutDev | Update;
1053+
} else {
1054+
workInProgress.flags |= Update;
1055+
}
10131056
}
10141057

10151058
// If shouldComponentUpdate returned false, we should still update the

packages/react-reconciler/src/ReactFiberCommitWork.new.js

+134
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
enableFundamentalAPI,
3737
enableSuspenseCallback,
3838
enableScopeAPI,
39+
enableDoubleInvokingEffects,
3940
} from 'shared/ReactFeatureFlags';
4041
import {
4142
FunctionComponent,
@@ -2436,11 +2437,144 @@ function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {
24362437
fiber.return = expectedReturnFiber;
24372438
}
24382439

2440+
function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
2441+
if (__DEV__ && enableDoubleInvokingEffects) {
2442+
// We don't need to re-check for legacy roots here.
2443+
// This function will not be called within legacy roots.
2444+
switch (fiber.tag) {
2445+
case FunctionComponent:
2446+
case ForwardRef:
2447+
case SimpleMemoComponent: {
2448+
invokeGuardedCallback(
2449+
null,
2450+
commitHookEffectListMount,
2451+
null,
2452+
HookLayout | HookHasEffect,
2453+
fiber,
2454+
);
2455+
if (hasCaughtError()) {
2456+
const mountError = clearCaughtError();
2457+
captureCommitPhaseError(fiber, mountError);
2458+
}
2459+
break;
2460+
}
2461+
case ClassComponent: {
2462+
const instance = fiber.stateNode;
2463+
invokeGuardedCallback(null, instance.componentDidMount, instance);
2464+
if (hasCaughtError()) {
2465+
const mountError = clearCaughtError();
2466+
captureCommitPhaseError(fiber, mountError);
2467+
}
2468+
break;
2469+
}
2470+
}
2471+
}
2472+
}
2473+
2474+
function invokePassiveEffectMountInDEV(fiber: Fiber): void {
2475+
if (__DEV__ && enableDoubleInvokingEffects) {
2476+
// We don't need to re-check for legacy roots here.
2477+
// This function will not be called within legacy roots.
2478+
switch (fiber.tag) {
2479+
case FunctionComponent:
2480+
case ForwardRef:
2481+
case SimpleMemoComponent: {
2482+
invokeGuardedCallback(
2483+
null,
2484+
commitHookEffectListMount,
2485+
null,
2486+
HookPassive | HookHasEffect,
2487+
fiber,
2488+
);
2489+
if (hasCaughtError()) {
2490+
const mountError = clearCaughtError();
2491+
captureCommitPhaseError(fiber, mountError);
2492+
}
2493+
break;
2494+
}
2495+
}
2496+
}
2497+
}
2498+
2499+
function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
2500+
if (__DEV__ && enableDoubleInvokingEffects) {
2501+
// We don't need to re-check for legacy roots here.
2502+
// This function will not be called within legacy roots.
2503+
switch (fiber.tag) {
2504+
case FunctionComponent:
2505+
case ForwardRef:
2506+
case SimpleMemoComponent: {
2507+
invokeGuardedCallback(
2508+
null,
2509+
commitHookEffectListUnmount,
2510+
null,
2511+
HookLayout | HookHasEffect,
2512+
fiber,
2513+
fiber.return,
2514+
);
2515+
if (hasCaughtError()) {
2516+
const unmountError = clearCaughtError();
2517+
captureCommitPhaseError(fiber, unmountError);
2518+
}
2519+
break;
2520+
}
2521+
case ClassComponent: {
2522+
const instance = fiber.stateNode;
2523+
if (typeof instance.componentWillUnmount === 'function') {
2524+
invokeGuardedCallback(
2525+
null,
2526+
safelyCallComponentWillUnmount,
2527+
null,
2528+
fiber,
2529+
instance,
2530+
fiber.return,
2531+
);
2532+
if (hasCaughtError()) {
2533+
const unmountError = clearCaughtError();
2534+
captureCommitPhaseError(fiber, unmountError);
2535+
}
2536+
}
2537+
break;
2538+
}
2539+
}
2540+
}
2541+
}
2542+
2543+
function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
2544+
if (__DEV__ && enableDoubleInvokingEffects) {
2545+
// We don't need to re-check for legacy roots here.
2546+
// This function will not be called within legacy roots.
2547+
switch (fiber.tag) {
2548+
case FunctionComponent:
2549+
case ForwardRef:
2550+
case SimpleMemoComponent: {
2551+
invokeGuardedCallback(
2552+
null,
2553+
commitHookEffectListUnmount,
2554+
null,
2555+
HookPassive | HookHasEffect,
2556+
fiber,
2557+
fiber.return,
2558+
);
2559+
if (hasCaughtError()) {
2560+
const unmountError = clearCaughtError();
2561+
captureCommitPhaseError(fiber, unmountError);
2562+
}
2563+
break;
2564+
}
2565+
}
2566+
}
2567+
}
2568+
24392569
export {
24402570
commitResetTextContent,
24412571
commitPlacement,
24422572
commitDeletion,
24432573
commitWork,
24442574
commitAttachRef,
24452575
commitDetachRef,
2576+
invokeLayoutEffectMountInDEV,
2577+
invokeLayoutEffectUnmountInDEV,
2578+
invokePassiveEffectMountInDEV,
2579+
invokePassiveEffectUnmountInDEV,
24462580
};

0 commit comments

Comments
 (0)