Skip to content

Commit 254cbdb

Browse files
authored
Add a temporary internal option to disable double useEffect in legacy strict mode (#26914)
<!-- Thanks for submitting a pull request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory. Before submitting a pull request, please make sure the following is done: 1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`. 2. Run `yarn` in the repository root. 3. If you've fixed a bug or added code that should be tested, add tests! 4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development. 5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`. 6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect". 7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`). 8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files. 9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`). 10. If you haven't already, complete the CLA. Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> We are upgrading React 17 codebase to React18, and `StrictMode` has been great for surfacing potential production bugs on React18 for class components. There are non-trivial number of test failures caused by double `useEffect` in StrictMode. To prioritize surfacing and fixing issues that will break in production now, we need a flag to turn off double `useEffect` for now in StrictMode temporarily. This is a Meta-only hack for rolling out `createRoot` and we will fast follow to remove it and use full strict mode. ## How did you test this change? <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. How exactly did you verify that your PR solves the issue you wanted to solve? If you leave this empty, your PR will very likely be closed. --> jest
1 parent e3fb7c1 commit 254cbdb

13 files changed

+77
-8
lines changed

packages/react-reconciler/src/ReactFiber.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
enableDebugTracing,
4040
enableFloat,
4141
enableHostSingletons,
42+
enableDO_NOT_USE_disableStrictPassiveEffect,
4243
} from 'shared/ReactFeatureFlags';
4344
import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
4445
import {ConcurrentRoot} from './ReactRootTags';
@@ -87,6 +88,7 @@ import {
8788
StrictLegacyMode,
8889
StrictEffectsMode,
8990
ConcurrentUpdatesByDefaultMode,
91+
NoStrictPassiveEffectsMode,
9092
} from './ReactTypeOfMode';
9193
import {
9294
REACT_FORWARD_REF_TYPE,
@@ -539,6 +541,12 @@ export function createFiberFromTypeAndProps(
539541
if ((mode & ConcurrentMode) !== NoMode) {
540542
// Strict effects should never run on legacy roots
541543
mode |= StrictEffectsMode;
544+
if (
545+
enableDO_NOT_USE_disableStrictPassiveEffect &&
546+
pendingProps.DO_NOT_USE_disableStrictPassiveEffect
547+
) {
548+
mode |= NoStrictPassiveEffectsMode;
549+
}
542550
}
543551
break;
544552
case REACT_PROFILER_TYPE:
@@ -752,6 +760,10 @@ export function createFiberFromOffscreen(
752760
lanes: Lanes,
753761
key: null | string,
754762
): Fiber {
763+
if (__DEV__) {
764+
// StrictMode in Offscreen should always run double passive effects
765+
mode &= ~NoStrictPassiveEffectsMode;
766+
}
755767
const fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
756768
fiber.elementType = REACT_OFFSCREEN_TYPE;
757769
fiber.lanes = lanes;

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
DebugTracingMode,
5959
StrictEffectsMode,
6060
StrictLegacyMode,
61+
NoStrictPassiveEffectsMode,
6162
} from './ReactTypeOfMode';
6263
import {
6364
NoLane,
@@ -2257,7 +2258,8 @@ function mountEffect(
22572258
): void {
22582259
if (
22592260
__DEV__ &&
2260-
(currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
2261+
(currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode &&
2262+
(currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode
22612263
) {
22622264
mountEffectImpl(
22632265
MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,

packages/react-reconciler/src/ReactTypeOfMode.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
export type TypeOfMode = number;
1111

12-
export const NoMode = /* */ 0b000000;
12+
export const NoMode = /* */ 0b0000000;
1313
// TODO: Remove ConcurrentMode by reading from the root tag instead
14-
export const ConcurrentMode = /* */ 0b000001;
15-
export const ProfileMode = /* */ 0b000010;
16-
export const DebugTracingMode = /* */ 0b000100;
17-
export const StrictLegacyMode = /* */ 0b001000;
18-
export const StrictEffectsMode = /* */ 0b010000;
19-
export const ConcurrentUpdatesByDefaultMode = /* */ 0b100000;
14+
export const ConcurrentMode = /* */ 0b0000001;
15+
export const ProfileMode = /* */ 0b0000010;
16+
export const DebugTracingMode = /* */ 0b0000100;
17+
export const StrictLegacyMode = /* */ 0b0001000;
18+
export const StrictEffectsMode = /* */ 0b0010000;
19+
export const ConcurrentUpdatesByDefaultMode = /* */ 0b0100000;
20+
export const NoStrictPassiveEffectsMode = /* */ 0b1000000;

packages/react-reconciler/src/__tests__/ReactOffscreenStrictMode-test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ describe('ReactOffscreenStrictMode', () => {
5555
]);
5656
});
5757

58+
// @gate __DEV__ && enableOffscreen
59+
it('should trigger strict effects when disableStrictPassiveEffect is presented on StrictMode', async () => {
60+
await act(() => {
61+
ReactNoop.render(
62+
<React.StrictMode DO_NOT_USE_disableStrictPassiveEffect={true}>
63+
<Offscreen>
64+
<Component label="A" />
65+
</Offscreen>
66+
</React.StrictMode>,
67+
);
68+
});
69+
70+
expect(log).toEqual([
71+
'A: render',
72+
'A: render',
73+
'A: useLayoutEffect mount',
74+
'A: useEffect mount',
75+
'A: useLayoutEffect unmount',
76+
'A: useEffect unmount',
77+
'A: useLayoutEffect mount',
78+
'A: useEffect mount',
79+
]);
80+
});
81+
5882
// @gate __DEV__ && enableOffscreen && useModernStrictMode
5983
it('should not trigger strict effects when offscreen is hidden', async () => {
6084
await act(() => {

packages/react/src/__tests__/ReactStrictMode-test.internal.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,28 @@ describe('ReactStrictMode', () => {
104104
]);
105105
});
106106

107+
// @gate enableDO_NOT_USE_disableStrictPassiveEffect
108+
it('should include legacy + strict effects mode, but not strict passive effect with disableStrictPassiveEffect', async () => {
109+
await act(() => {
110+
const container = document.createElement('div');
111+
const root = ReactDOMClient.createRoot(container);
112+
root.render(
113+
<React.StrictMode DO_NOT_USE_disableStrictPassiveEffect={true}>
114+
<Component label="A" />
115+
</React.StrictMode>,
116+
);
117+
});
118+
119+
expect(log).toEqual([
120+
'A: render',
121+
'A: render',
122+
'A: useLayoutEffect mount',
123+
'A: useEffect mount',
124+
'A: useLayoutEffect unmount',
125+
'A: useLayoutEffect mount',
126+
]);
127+
});
128+
107129
it('should allow level to be increased with nesting', async () => {
108130
await act(() => {
109131
const container = document.createElement('div');

packages/shared/ReactFeatureFlags.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,4 @@ export const consoleManagedByDevToolsDuringStrictMode = true;
237237
// components will encounter in production, especially when used With <Offscreen />.
238238
// TODO: clean up legacy <StrictMode /> once tests pass WWW.
239239
export const useModernStrictMode = false;
240+
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const enableFloat = true;
8282
export const enableHostSingletons = true;
8383

8484
export const useModernStrictMode = false;
85+
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
8586
export const enableFizzExternalRuntime = false;
8687

8788
export const diffInCommitPhase = true;

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const enableFloat = true;
6868
export const enableHostSingletons = true;
6969

7070
export const useModernStrictMode = false;
71+
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
7172
export const enableFizzExternalRuntime = false;
7273
export const enableDeferRootSchedulingToMicrotask = true;
7374

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const enableFloat = true;
6868
export const enableHostSingletons = true;
6969

7070
export const useModernStrictMode = false;
71+
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
7172
export const enableFizzExternalRuntime = false;
7273
export const enableDeferRootSchedulingToMicrotask = true;
7374

packages/shared/forks/ReactFeatureFlags.test-renderer.native.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const enableFloat = true;
6666
export const enableHostSingletons = true;
6767

6868
export const useModernStrictMode = false;
69+
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
6970
export const enableDeferRootSchedulingToMicrotask = true;
7071

7172
export const diffInCommitPhase = true;

0 commit comments

Comments
 (0)