From 5fce6488ce1dc97e31515a47ff409d32ab722d65 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 21 Jan 2019 20:28:36 +0000 Subject: [PATCH] Revert "Disallow reading context during useMemo etc" (#14651) * Revert "Add test coverage for readContext() on the server (#14649)" This reverts commit fe2ecd276e9b9e57a49ddf2d86ff01677b69b493. * Revert "Warn about incorrect use of useImperativeHandle() (#14647)" This reverts commit 8f45a7fdc469c6d88ca5ca105095b03dd4e862e1. * Revert "Disallow reading context during useMemo etc (#14648)" This reverts commit 1fcbd22431d97e404c1f684744dc543b6c22b076. --- .../src/ReactFiberClassComponent.js | 9 +- .../src/ReactFiberDispatcher.js | 2 +- .../react-reconciler/src/ReactFiberHooks.js | 29 +------ .../src/__tests__/ReactHooks-test.internal.js | 82 ------------------- 4 files changed, 12 insertions(+), 110 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 3875371714f38..16a3e39fc316b 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -20,6 +20,7 @@ import { import ReactStrictModeWarnings from './ReactStrictModeWarnings'; import {isMounted} from 'react-reconciler/reflection'; import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; import shallowEqual from 'shared/shallowEqual'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -47,7 +48,6 @@ import { hasContextChanged, emptyContextObject, } from './ReactFiberContext'; -import {readContext} from './ReactFiberNewContext'; import { requestCurrentTime, computeExpirationForFiber, @@ -55,6 +55,13 @@ import { flushPassiveEffects, } from './ReactFiberScheduler'; +const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; + +function readContext(contextType: any): any { + const dispatcher = ReactCurrentDispatcher.current; + return dispatcher.readContext(contextType); +} + const fakeInternalInstance = {}; const isArray = Array.isArray; diff --git a/packages/react-reconciler/src/ReactFiberDispatcher.js b/packages/react-reconciler/src/ReactFiberDispatcher.js index ad1f2b3e51c77..ed2ed3b2f315b 100644 --- a/packages/react-reconciler/src/ReactFiberDispatcher.js +++ b/packages/react-reconciler/src/ReactFiberDispatcher.js @@ -7,8 +7,8 @@ * @flow */ +import {readContext} from './ReactFiberNewContext'; import { - readContext, useCallback, useContext, useEffect, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 916094187e4a4..cbd2a255bbde7 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -14,7 +14,7 @@ import type {HookEffectTag} from './ReactHookEffectTags'; import {NoWork} from './ReactFiberExpirationTime'; import {enableHooks} from 'shared/ReactFeatureFlags'; -import {readContext as readContextWithoutCheck} from './ReactFiberNewContext'; +import {readContext} from './ReactFiberNewContext'; import { Update as UpdateEffect, Passive as PassiveEffect, @@ -284,7 +284,7 @@ export function resetHooks(): void { // This is used to reset the state of this module when a component throws. // It's also called inside mountIndeterminateComponent if we determine the - // component is a module-style component, and also in readContext() above. + // component is a module-style component. renderExpirationTime = NoWork; currentlyRenderingFiber = null; @@ -394,7 +394,7 @@ export function useContext( // Ensure we're in a function component (class components support only the // .unstable_read() form) resolveCurrentlyRenderingFiber(); - return readContextWithoutCheck(context, observedBits); + return readContext(context, observedBits); } export function useState( @@ -785,29 +785,6 @@ export function useMemo( return nextValue; } -export function readContext( - context: ReactContext, - observedBits: void | number | boolean, -): T { - // Forbid reading context inside Hooks. - // The outer check tells us whether we're inside a Hook like useMemo(). - // However, it would also be true if we're rendering a class. - if (currentlyRenderingFiber === null) { - // The inner check tells us we're currently in renderWithHooks() phase - // rather than, for example, in a class or a context consumer. - // Then we know it should be an error. - if (renderExpirationTime !== NoWork) { - invariant( - false, - 'Context can only be read inside the body of a component. ' + - 'If you read context inside a Hook like useMemo or useReducer, ' + - 'move the call directly into the component body.', - ); - } - } - return readContextWithoutCheck(context, observedBits); -} - function dispatchAction( fiber: Fiber, queue: UpdateQueue, diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index 3233666776707..0008a27b14850 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -672,88 +672,6 @@ describe('ReactHooks', () => { expect(root.toJSON()).toEqual('123'); }); - it('throws when reading context inside useMemo', () => { - const {useMemo, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - return useMemo(() => { - return ReactCurrentDispatcher.current.readContext(ThemeContext); - }, []); - } - - expect(() => ReactTestRenderer.create()).toThrow( - 'Context can only be read inside the body of a component', - ); - }); - - it('throws when reading context inside useEffect', () => { - const {useEffect, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useEffect(() => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }); - return null; - } - - const root = ReactTestRenderer.create(); - expect(() => root.update()).toThrow( - // The exact message doesn't matter, just make sure we don't allow this - "Cannot read property 'readContext' of null", - ); - }); - - it('throws when reading context inside useLayoutEffect', () => { - const {useLayoutEffect, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useLayoutEffect(() => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }); - return null; - } - - expect(() => ReactTestRenderer.create()).toThrow( - // The exact message doesn't matter, just make sure we don't allow this - "Cannot read property 'readContext' of null", - ); - }); - - it('throws when reading context inside useReducer', () => { - const {useReducer, createContext} = React; - const ReactCurrentDispatcher = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .ReactCurrentDispatcher; - - const ThemeContext = createContext('light'); - function App() { - useReducer( - () => { - ReactCurrentDispatcher.current.readContext(ThemeContext); - }, - null, - {}, - ); - return null; - } - - expect(() => ReactTestRenderer.create()).toThrow( - 'Context can only be read inside the body of a component.', - ); - }); - it('throws when calling hooks inside useReducer', () => { const {useReducer, useRef} = React; function App() {