Skip to content

Commit e9a4a44

Browse files
authored
Add back root override for strict mode (#21428)
* Add back root override for strict mode * Switch flag to boolean * Fix flow
1 parent d1542de commit e9a4a44

File tree

13 files changed

+74
-5
lines changed

13 files changed

+74
-5
lines changed

packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js

+14
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ describe('ReactTestUtils.act()', () => {
102102
root.render(<App />);
103103
Scheduler.unstable_flushAll();
104104
});
105+
106+
// @gate experimental
107+
it('warns in concurrent mode if root is strict', () => {
108+
expect(() => {
109+
const root = ReactDOM.unstable_createRoot(
110+
document.createElement('div'),
111+
{unstable_strictMode: true},
112+
);
113+
root.render(<App />);
114+
Scheduler.unstable_flushAll();
115+
}).toErrorDev([
116+
'An update to App ran an effect, but was not wrapped in act(...)',
117+
]);
118+
});
105119
});
106120
});
107121

packages/react-dom/src/client/ReactDOMRoot.js

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type RootOptions = {
2727
mutableSources?: Array<MutableSource<any>>,
2828
...
2929
},
30+
unstable_strictMode?: boolean,
3031
unstable_concurrentUpdatesByDefault?: boolean,
3132
...
3233
};
@@ -122,6 +123,7 @@ function createRootImpl(
122123
options.hydrationOptions != null &&
123124
options.hydrationOptions.mutableSources) ||
124125
null;
126+
const isStrictMode = options != null && options.unstable_strictMode === true;
125127

126128
let concurrentUpdatesByDefaultOverride = null;
127129
if (allowConcurrentByDefault) {
@@ -136,6 +138,7 @@ function createRootImpl(
136138
tag,
137139
hydrate,
138140
hydrationCallbacks,
141+
isStrictMode,
139142
concurrentUpdatesByDefaultOverride,
140143
);
141144
markContainerAsRoot(root.current, container);

packages/react-native-renderer/src/ReactFabric.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ function render(
207207
if (!root) {
208208
// TODO (bvaughn): If we decide to keep the wrapper component,
209209
// We could create a wrapper for containerTag as well to reduce special casing.
210-
root = createContainer(containerTag, LegacyRoot, false, null, null);
210+
root = createContainer(containerTag, LegacyRoot, false, null, false, null);
211211
roots.set(containerTag, root);
212212
}
213213
updateContainer(element, root, null, callback);

packages/react-native-renderer/src/ReactNativeRenderer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function render(
203203
if (!root) {
204204
// TODO (bvaughn): If we decide to keep the wrapper component,
205205
// We could create a wrapper for containerTag as well to reduce special casing.
206-
root = createContainer(containerTag, LegacyRoot, false, null, null);
206+
root = createContainer(containerTag, LegacyRoot, false, null, false, null);
207207
roots.set(containerTag, root);
208208
}
209209
updateContainer(element, root, null, callback);

packages/react-noop-renderer/src/createReactNoop.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
722722
if (!root) {
723723
const container = {rootID: rootID, pendingChildren: [], children: []};
724724
rootContainers.set(rootID, container);
725-
root = NoopRenderer.createContainer(container, tag, false, null);
725+
root = NoopRenderer.createContainer(container, tag, false, null, null);
726726
roots.set(rootID, root);
727727
}
728728
return root.current.stateNode.containerInfo;
@@ -740,6 +740,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
740740
ConcurrentRoot,
741741
false,
742742
null,
743+
null,
743744
);
744745
return {
745746
_Scheduler: Scheduler,
@@ -766,6 +767,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
766767
LegacyRoot,
767768
false,
768769
null,
770+
null,
769771
);
770772
return {
771773
_Scheduler: Scheduler,

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,19 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
422422

423423
export function createHostRootFiber(
424424
tag: RootTag,
425+
isStrictMode: boolean,
425426
concurrentUpdatesByDefaultOverride: null | boolean,
426427
): Fiber {
427428
let mode;
428429
if (tag === ConcurrentRoot) {
429430
mode = ConcurrentMode;
430-
if (enableStrictEffects && createRootStrictEffectsByDefault) {
431+
if (isStrictMode === true) {
432+
mode |= StrictLegacyMode;
433+
434+
if (enableStrictEffects) {
435+
mode |= StrictEffectsMode;
436+
}
437+
} else if (enableStrictEffects && createRootStrictEffectsByDefault) {
431438
mode |= StrictLegacyMode | StrictEffectsMode;
432439
}
433440
if (

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,19 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
422422

423423
export function createHostRootFiber(
424424
tag: RootTag,
425+
isStrictMode: boolean,
425426
concurrentUpdatesByDefaultOverride: null | boolean,
426427
): Fiber {
427428
let mode;
428429
if (tag === ConcurrentRoot) {
429430
mode = ConcurrentMode;
430-
if (enableStrictEffects && createRootStrictEffectsByDefault) {
431+
if (isStrictMode === true) {
432+
mode |= StrictLegacyMode;
433+
434+
if (enableStrictEffects) {
435+
mode |= StrictEffectsMode;
436+
}
437+
} else if (enableStrictEffects && createRootStrictEffectsByDefault) {
431438
mode |= StrictLegacyMode | StrictEffectsMode;
432439
}
433440
if (

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

+2
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,15 @@ export function createContainer(
248248
tag: RootTag,
249249
hydrate: boolean,
250250
hydrationCallbacks: null | SuspenseHydrationCallbacks,
251+
isStrictMode: boolean,
251252
concurrentUpdatesByDefaultOverride: null | boolean,
252253
): OpaqueRoot {
253254
return createFiberRoot(
254255
containerInfo,
255256
tag,
256257
hydrate,
257258
hydrationCallbacks,
259+
isStrictMode,
258260
concurrentUpdatesByDefaultOverride,
259261
);
260262
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,15 @@ export function createContainer(
248248
tag: RootTag,
249249
hydrate: boolean,
250250
hydrationCallbacks: null | SuspenseHydrationCallbacks,
251+
isStrictMode: boolean,
251252
concurrentUpdatesByDefaultOverride: null | boolean,
252253
): OpaqueRoot {
253254
return createFiberRoot(
254255
containerInfo,
255256
tag,
256257
hydrate,
257258
hydrationCallbacks,
259+
isStrictMode,
258260
concurrentUpdatesByDefaultOverride,
259261
);
260262
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export function createFiberRoot(
9898
tag: RootTag,
9999
hydrate: boolean,
100100
hydrationCallbacks: null | SuspenseHydrationCallbacks,
101+
isStrictMode: boolean,
101102
concurrentUpdatesByDefaultOverride: null | boolean,
102103
): FiberRoot {
103104
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
@@ -109,6 +110,7 @@ export function createFiberRoot(
109110
// stateNode is any.
110111
const uninitializedFiber = createHostRootFiber(
111112
tag,
113+
isStrictMode,
112114
concurrentUpdatesByDefaultOverride,
113115
);
114116
root.current = uninitializedFiber;

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

+2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export function createFiberRoot(
9898
tag: RootTag,
9999
hydrate: boolean,
100100
hydrationCallbacks: null | SuspenseHydrationCallbacks,
101+
isStrictMode: boolean,
101102
concurrentUpdatesByDefaultOverride: null | boolean,
102103
): FiberRoot {
103104
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
@@ -109,6 +110,7 @@ export function createFiberRoot(
109110
// stateNode is any.
110111
const uninitializedFiber = createHostRootFiber(
111112
tag,
113+
isStrictMode,
112114
concurrentUpdatesByDefaultOverride,
113115
);
114116
root.current = uninitializedFiber;

packages/react-test-renderer/src/ReactTestRenderer.js

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const {IsSomeRendererActing} = ReactSharedInternals;
5858
type TestRendererOptions = {
5959
createNodeMock: (element: React$Element<any>) => any,
6060
unstable_isConcurrent: boolean,
61+
unstable_strictMode: boolean,
6162
unstable_concurrentUpdatesByDefault: boolean,
6263
...
6364
};
@@ -436,6 +437,7 @@ function propsMatch(props: Object, filter: Object): boolean {
436437
function create(element: React$Element<any>, options: TestRendererOptions) {
437438
let createNodeMock = defaultTestOptions.createNodeMock;
438439
let isConcurrent = false;
440+
let isStrictMode = false;
439441
let concurrentUpdatesByDefault = null;
440442
if (typeof options === 'object' && options !== null) {
441443
if (typeof options.createNodeMock === 'function') {
@@ -444,6 +446,9 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
444446
if (options.unstable_isConcurrent === true) {
445447
isConcurrent = true;
446448
}
449+
if (options.unstable_strictMode === true) {
450+
isStrictMode = true;
451+
}
447452
if (allowConcurrentByDefault) {
448453
if (options.unstable_concurrentUpdatesByDefault !== undefined) {
449454
concurrentUpdatesByDefault =
@@ -461,6 +466,7 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
461466
isConcurrent ? ConcurrentRoot : LegacyRoot,
462467
false,
463468
null,
469+
isStrictMode,
464470
concurrentUpdatesByDefault,
465471
);
466472
invariant(root != null, 'something went wrong');

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

+22
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,28 @@ describe('ReactStrictMode', () => {
6565
});
6666

6767
if (__DEV__) {
68+
// @gate experimental
69+
it('should support enabling strict mode via createRoot option', () => {
70+
act(() => {
71+
const container = document.createElement('div');
72+
const root = ReactDOM.createRoot(container, {
73+
unstable_strictMode: true,
74+
});
75+
root.render(<Component label="A" />);
76+
});
77+
78+
expect(log).toEqual([
79+
'A: render',
80+
'A: render',
81+
'A: useLayoutEffect mount',
82+
'A: useEffect mount',
83+
'A: useLayoutEffect unmount',
84+
'A: useEffect unmount',
85+
'A: useLayoutEffect mount',
86+
'A: useEffect mount',
87+
]);
88+
});
89+
6890
// @gate experimental
6991
it('should include legacy + strict effects mode', () => {
7092
act(() => {

0 commit comments

Comments
 (0)