Skip to content

Commit

Permalink
Add back root override for strict mode
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii committed May 4, 2021
1 parent d1542de commit 304a5b2
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 5 deletions.
14 changes: 14 additions & 0 deletions packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ describe('ReactTestUtils.act()', () => {
root.render(<App />);
Scheduler.unstable_flushAll();
});

// @gate experimental
it('warns in concurrent mode if root is strict', () => {
expect(() => {
const root = ReactDOM.unstable_createRoot(
document.createElement('div'),
{unstable_strictMode: true},
);
root.render(<App />);
Scheduler.unstable_flushAll();
}).toErrorDev([
'An update to App ran an effect, but was not wrapped in act(...)',
]);
});
});
});

Expand Down
4 changes: 4 additions & 0 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type RootOptions = {
mutableSources?: Array<MutableSource<any>>,
...
},
unstable_strictMode?: boolean,
unstable_concurrentUpdatesByDefault?: boolean,
...
};
Expand Down Expand Up @@ -122,6 +123,8 @@ function createRootImpl(
options.hydrationOptions != null &&
options.hydrationOptions.mutableSources) ||
null;
const strictModeLevelOverride =
options != null && options.unstable_strictMode === true ? 2 : null;

let concurrentUpdatesByDefaultOverride = null;
if (allowConcurrentByDefault) {
Expand All @@ -136,6 +139,7 @@ function createRootImpl(
tag,
hydrate,
hydrationCallbacks,
strictModeLevelOverride,
concurrentUpdatesByDefaultOverride,
);
markContainerAsRoot(root.current, container);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ function render(
if (!root) {
// TODO (bvaughn): If we decide to keep the wrapper component,
// We could create a wrapper for containerTag as well to reduce special casing.
root = createContainer(containerTag, LegacyRoot, false, null, null);
root = createContainer(containerTag, LegacyRoot, false, null, null, null);
roots.set(containerTag, root);
}
updateContainer(element, root, null, callback);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-renderer/src/ReactNativeRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ function render(
if (!root) {
// TODO (bvaughn): If we decide to keep the wrapper component,
// We could create a wrapper for containerTag as well to reduce special casing.
root = createContainer(containerTag, LegacyRoot, false, null, null);
root = createContainer(containerTag, LegacyRoot, false, null, null, null);
roots.set(containerTag, root);
}
updateContainer(element, root, null, callback);
Expand Down
4 changes: 3 additions & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
if (!root) {
const container = {rootID: rootID, pendingChildren: [], children: []};
rootContainers.set(rootID, container);
root = NoopRenderer.createContainer(container, tag, false, null);
root = NoopRenderer.createContainer(container, tag, false, null, null);
roots.set(rootID, root);
}
return root.current.stateNode.containerInfo;
Expand All @@ -740,6 +740,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
ConcurrentRoot,
false,
null,
null,
);
return {
_Scheduler: Scheduler,
Expand All @@ -766,6 +767,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
LegacyRoot,
false,
null,
null,
);
return {
_Scheduler: Scheduler,
Expand Down
12 changes: 11 additions & 1 deletion packages/react-reconciler/src/ReactFiber.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,22 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {

export function createHostRootFiber(
tag: RootTag,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): Fiber {
let mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode;
if (enableStrictEffects && createRootStrictEffectsByDefault) {
if (strictModeLevelOverride !== null) {
if (strictModeLevelOverride >= 1) {
mode |= StrictLegacyMode;
}
if (enableStrictEffects) {
if (strictModeLevelOverride >= 2) {
mode |= StrictEffectsMode;
}
}
} else if (enableStrictEffects && createRootStrictEffectsByDefault) {
mode |= StrictLegacyMode | StrictEffectsMode;
}
if (
Expand Down
12 changes: 11 additions & 1 deletion packages/react-reconciler/src/ReactFiber.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,22 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {

export function createHostRootFiber(
tag: RootTag,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): Fiber {
let mode;
if (tag === ConcurrentRoot) {
mode = ConcurrentMode;
if (enableStrictEffects && createRootStrictEffectsByDefault) {
if (strictModeLevelOverride !== null) {
if (strictModeLevelOverride >= 1) {
mode |= StrictLegacyMode;
}
if (enableStrictEffects) {
if (strictModeLevelOverride >= 2) {
mode |= StrictEffectsMode;
}
}
} else if (enableStrictEffects && createRootStrictEffectsByDefault) {
mode |= StrictLegacyMode | StrictEffectsMode;
}
if (
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactFiberReconciler.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,15 @@ export function createContainer(
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): OpaqueRoot {
return createFiberRoot(
containerInfo,
tag,
hydrate,
hydrationCallbacks,
strictModeLevelOverride,
concurrentUpdatesByDefaultOverride,
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactFiberReconciler.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,15 @@ export function createContainer(
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): OpaqueRoot {
return createFiberRoot(
containerInfo,
tag,
hydrate,
hydrationCallbacks,
strictModeLevelOverride,
concurrentUpdatesByDefaultOverride,
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactFiberRoot.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export function createFiberRoot(
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): FiberRoot {
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
Expand All @@ -109,6 +110,7 @@ export function createFiberRoot(
// stateNode is any.
const uninitializedFiber = createHostRootFiber(
tag,
strictModeLevelOverride,
concurrentUpdatesByDefaultOverride,
);
root.current = uninitializedFiber;
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactFiberRoot.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export function createFiberRoot(
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
strictModeLevelOverride: null | number,
concurrentUpdatesByDefaultOverride: null | boolean,
): FiberRoot {
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
Expand All @@ -109,6 +110,7 @@ export function createFiberRoot(
// stateNode is any.
const uninitializedFiber = createHostRootFiber(
tag,
strictModeLevelOverride,
concurrentUpdatesByDefaultOverride,
);
root.current = uninitializedFiber;
Expand Down
6 changes: 6 additions & 0 deletions packages/react-test-renderer/src/ReactTestRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const {IsSomeRendererActing} = ReactSharedInternals;
type TestRendererOptions = {
createNodeMock: (element: React$Element<any>) => any,
unstable_isConcurrent: boolean,
unstable_strictMode: boolean,
unstable_concurrentUpdatesByDefault: boolean,
...
};
Expand Down Expand Up @@ -436,6 +437,7 @@ function propsMatch(props: Object, filter: Object): boolean {
function create(element: React$Element<any>, options: TestRendererOptions) {
let createNodeMock = defaultTestOptions.createNodeMock;
let isConcurrent = false;
let strictModeLevel = null;
let concurrentUpdatesByDefault = null;
if (typeof options === 'object' && options !== null) {
if (typeof options.createNodeMock === 'function') {
Expand All @@ -444,6 +446,9 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
if (options.unstable_isConcurrent === true) {
isConcurrent = true;
}
if (options.unstable_strictMode === true) {
strictModeLevel = 2;
}
if (allowConcurrentByDefault) {
if (options.unstable_concurrentUpdatesByDefault !== undefined) {
concurrentUpdatesByDefault =
Expand All @@ -461,6 +466,7 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
isConcurrent ? ConcurrentRoot : LegacyRoot,
false,
null,
strictModeLevel,
concurrentUpdatesByDefault,
);
invariant(root != null, 'something went wrong');
Expand Down
22 changes: 22 additions & 0 deletions packages/react/src/__tests__/ReactStrictMode-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ describe('ReactStrictMode', () => {
]);
});

// @gate experimental
it('should support enabling strict mode via createRoot option', () => {
act(() => {
const container = document.createElement('div');
const root = ReactDOM.createRoot(container, {
unstable_strictMode: true,
});
root.render(<Component label="A" />);
});

expect(log).toEqual([
'A: render',
'A: render',
'A: useLayoutEffect mount',
'A: useEffect mount',
'A: useLayoutEffect unmount',
'A: useEffect unmount',
'A: useLayoutEffect mount',
'A: useEffect mount',
]);
});

if (__DEV__) {
// @gate experimental
it('should include legacy + strict effects mode', () => {
Expand Down

0 comments on commit 304a5b2

Please sign in to comment.