diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index fd8c93253b1e8..538748cd0a29c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -887,7 +887,13 @@ function dispatchAction( if (eagerReducer !== null) { try { const currentState: S = (queue.eagerState: any); + // Temporarily clear to forbid calling Hooks in a reducer. + let maybeFiber = currentlyRenderingFiber; // Note: likely null now unlike `fiber` + currentlyRenderingFiber = null; + stashContextDependencies(); const eagerState = eagerReducer(currentState, action); + currentlyRenderingFiber = maybeFiber; + unstashContextDependencies(); // Stash the eagerly computed state, and the reducer used to compute // it, on the update object. If the reducer hasn't changed by the // time we enter the render phase, then the eager state can be used diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index 0eae5155e77a8..b8e7e8d7d8a79 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -778,6 +778,41 @@ describe('ReactHooks', () => { ); }); + // Edge case. + it('throws when reading context inside eager useReducer', () => { + const {useState, createContext} = React; + const ThemeContext = createContext('light'); + + const ReactCurrentDispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentDispatcher; + + let _setState; + function Fn() { + const [, setState] = useState(0); + _setState = setState; + return null; + } + + class Cls extends React.Component { + render() { + _setState(() => { + ReactCurrentDispatcher.current.readContext(ThemeContext); + }); + return null; + } + } + + expect(() => + ReactTestRenderer.create( + + + + , + ), + ).toThrow('Context can only be read while React is rendering'); + }); + it('throws when calling hooks inside useReducer', () => { const {useReducer, useRef} = React; function App() {