Ensure useState and useReducer initializer functions are double invoked in StrictMode (#28248)

This commit is contained in:
Sebastian Silbermann
2024-02-06 17:53:08 +01:00
committed by GitHub
parent 560b0ddf01
commit 82e47e83ca
2 changed files with 53 additions and 1 deletions
+13 -1
View File
@@ -1154,6 +1154,11 @@ function mountReducer<S, I, A>(
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
init(initialArg);
setIsStrictModeForDevtools(false);
}
} else {
initialState = ((initialArg: any): S);
}
@@ -1745,8 +1750,15 @@ function forceStoreRerender(fiber: Fiber) {
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
const initialStateInitializer = initialState;
// $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
initialState = initialState();
initialState = initialStateInitializer();
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
// $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
initialStateInitializer();
setIsStrictModeForDevtools(false);
}
}
hook.memoizedState = hook.baseState = initialState;
const queue: UpdateQueue<S, BasicStateAction<S>> = {
@@ -202,6 +202,46 @@ describe('ReactStrictMode', () => {
expect(instance.state.count).toBe(2);
});
// @gate debugRenderPhaseSideEffectsForStrictMode
it('double invokes useState and useReducer initializers functions', async () => {
const log = [];
function App() {
React.useState(() => {
log.push('Compute initial state count: 1');
return 1;
});
React.useReducer(
s => s,
2,
s => {
log.push('Compute initial reducer count: 2');
return s;
},
);
return 3;
}
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
});
expect(container.textContent).toBe('3');
expect(log).toEqual([
'Compute initial state count: 1',
'Compute initial state count: 1',
'Compute initial reducer count: 2',
'Compute initial reducer count: 2',
]);
});
it('should invoke only precommit lifecycle methods twice in DEV legacy roots', async () => {
const {StrictMode} = React;