Skip to content

Commit 20b0c01

Browse files
committed
Add an option in StrictMode to disable double useEffect in legacy strict mode
1 parent e1ad4aa commit 20b0c01

File tree

5 files changed

+64
-8
lines changed

5 files changed

+64
-8
lines changed

packages/react-reconciler/src/ReactFiber.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import {
8787
StrictLegacyMode,
8888
StrictEffectsMode,
8989
ConcurrentUpdatesByDefaultMode,
90+
NoStrictPassiveEffectsMode,
9091
} from './ReactTypeOfMode';
9192
import {
9293
REACT_FORWARD_REF_TYPE,
@@ -539,6 +540,9 @@ export function createFiberFromTypeAndProps(
539540
if ((mode & ConcurrentMode) !== NoMode) {
540541
// Strict effects should never run on legacy roots
541542
mode |= StrictEffectsMode;
543+
if (pendingProps.unstable_disableStrictPassiveEffect) {
544+
mode |= NoStrictPassiveEffectsMode;
545+
}
542546
}
543547
break;
544548
case REACT_PROFILER_TYPE:
@@ -752,6 +756,10 @@ export function createFiberFromOffscreen(
752756
lanes: Lanes,
753757
key: null | string,
754758
): Fiber {
759+
if (__DEV__) {
760+
// StrictMode in Offscreen should always run double passive effects
761+
mode &= ~NoStrictPassiveEffectsMode;
762+
}
755763
const fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
756764
fiber.elementType = REACT_OFFSCREEN_TYPE;
757765
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 unstable_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: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,27 @@ describe('ReactStrictMode', () => {
104104
]);
105105
});
106106

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

0 commit comments

Comments
 (0)