From 306e3034f7d90c79f84bf99ace7051c934e12210 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 16 Oct 2018 14:10:05 -0400 Subject: [PATCH 1/2] Remove unstable_read() in favor of direct dispatcher call --- .../src/ReactFiberClassComponent.js | 18 +++++-- .../src/ReactFiberNewContext.js | 2 +- .../ReactNewContext-test.internal.js | 52 ++++++------------- .../src/__tests__/ReactPure-test.internal.js | 9 +++- packages/react/src/ReactContext.js | 31 ----------- packages/shared/ReactTypes.js | 1 - 6 files changed, 38 insertions(+), 75 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 7420c7dc3dfe..c2a87d8e9947 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 * as ReactInstanceMap from 'shared/ReactInstanceMap'; +import ReactSharedInternals from 'shared/ReactSharedInternals'; import shallowEqual from 'shared/shallowEqual'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -50,6 +51,13 @@ import { scheduleWork, } from './ReactFiberScheduler'; +const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; + +function readContext(contextType: any): any { + const dispatcher = ReactCurrentOwner.currentDispatcher; + return dispatcher.readContext(contextType); +} + const fakeInternalInstance = {}; const isArray = Array.isArray; @@ -508,7 +516,7 @@ function constructClassInstance( if (typeof contextType === 'object' && contextType !== null) { if (__DEV__) { if ( - typeof contextType.unstable_read !== 'function' && + contextType.Consumer === undefined && !didWarnAboutInvalidateContextType.has(ctor) ) { didWarnAboutInvalidateContextType.add(ctor); @@ -522,7 +530,7 @@ function constructClassInstance( } } - context = (contextType: any).unstable_read(); + context = readContext((contextType: any)); } else { unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); const contextTypes = ctor.contextTypes; @@ -725,7 +733,7 @@ function mountClassInstance( const contextType = ctor.contextType; if (typeof contextType === 'object' && contextType !== null) { - instance.context = (contextType: any).unstable_read(); + instance.context = readContext(contextType); } else { const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); instance.context = getMaskedContext(workInProgress, unmaskedContext); @@ -833,7 +841,7 @@ function resumeMountClassInstance( const contextType = ctor.contextType; let nextContext; if (typeof contextType === 'object' && contextType !== null) { - nextContext = (contextType: any).unstable_read(); + nextContext = readContext(contextType); } else { const nextLegacyUnmaskedContext = getUnmaskedContext( workInProgress, @@ -979,7 +987,7 @@ function updateClassInstance( const contextType = ctor.contextType; let nextContext; if (typeof contextType === 'object' && contextType !== null) { - nextContext = (contextType: any).unstable_read(); + nextContext = readContext(contextType); } else { const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true); nextContext = getMaskedContext(workInProgress, nextUnmaskedContext); diff --git a/packages/react-reconciler/src/ReactFiberNewContext.js b/packages/react-reconciler/src/ReactFiberNewContext.js index 0de11bc0f29e..fd4a739bfcf3 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.js @@ -302,7 +302,7 @@ export function readContext( if (lastContextDependency === null) { invariant( currentlyRenderingFiber !== null, - 'Context.unstable_read(): Context can only be read while React is ' + + 'Context can only be read while React is ' + 'rendering, e.g. inside the render method or getDerivedStateFromProps.', ); // This is the first dependency in the list diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js index 52ee23f5ffa1..4e0e5cfa45c1 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js @@ -39,26 +39,33 @@ describe('ReactNewContext', () => { return {type: 'span', children: [], prop}; } + function readContext(Context, observedBits) { + const dispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner + .currentDispatcher; + return dispatcher.readContext(Context, observedBits); + } + // We have several ways of reading from context. sharedContextTests runs // a suite of tests for a given context consumer implementation. sharedContextTests('Context.Consumer', Context => Context.Consumer); sharedContextTests( - 'Context.unstable_read inside function component', + 'readContext(Context) inside function component', Context => function Consumer(props) { const observedBits = props.unstable_observedBits; - const contextValue = Context.unstable_read(observedBits); + const contextValue = readContext(Context, observedBits); const render = props.children; return render(contextValue); }, ); sharedContextTests( - 'Context.unstable_read inside class component', + 'readContext(Context) inside class component', Context => class Consumer extends React.Component { render() { const observedBits = this.props.unstable_observedBits; - const contextValue = Context.unstable_read(observedBits); + const contextValue = readContext(Context, observedBits); const render = this.props.children; return render(contextValue); } @@ -1192,7 +1199,7 @@ describe('ReactNewContext', () => { return ( {foo => { - const bar = BarContext.unstable_read(); + const bar = readContext(BarContext); return ; }} @@ -1236,7 +1243,7 @@ describe('ReactNewContext', () => { }); }); - describe('unstable_readContext', () => { + describe('readContext', () => { it('can use the same context multiple times in the same function', () => { const Context = React.createContext({foo: 0, bar: 0, baz: 0}, (a, b) => { let result = 0; @@ -1262,13 +1269,13 @@ describe('ReactNewContext', () => { } function FooAndBar() { - const {foo} = Context.unstable_read(0b001); - const {bar} = Context.unstable_read(0b010); + const {foo} = readContext(Context, 0b001); + const {bar} = readContext(Context, 0b010); return ; } function Baz() { - const {baz} = Context.unstable_read(0b100); + const {baz} = readContext(Context, 0b100); return ; } @@ -1631,33 +1638,6 @@ Context fuzz tester error! Copy and paste the following line into the test suite ); }); - it('should warn with an error message when using Context.Consumer.unstable_read() DEV', () => { - const BarContext = React.createContext({value: 'bar-initial'}); - - function Child() { - let value = BarContext.Consumer.unstable_read(); - return
; - } - - function Component() { - return ( - - - - - - ); - } - - expect(() => { - ReactNoop.render(); - ReactNoop.flush(); - }).toWarnDev( - 'Calling Context.Consumer.unstable_read() is not supported and will be removed in ' + - 'a future major release. Did you mean to render Context.unstable_read() instead?', - ); - }); - it('should warn with an error message when using Context.Consumer.Provider DEV', () => { const BarContext = React.createContext({value: 'bar-initial'}); diff --git a/packages/react-reconciler/src/__tests__/ReactPure-test.internal.js b/packages/react-reconciler/src/__tests__/ReactPure-test.internal.js index 3d5329359991..b30ffaa1632c 100644 --- a/packages/react-reconciler/src/__tests__/ReactPure-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactPure-test.internal.js @@ -90,8 +90,15 @@ describe('pure', () => { const CountContext = React.createContext(0); + function readContext(Context) { + const dispatcher = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED + .ReactCurrentOwner.currentDispatcher; + return dispatcher.readContext(Context); + } + function Counter(props) { - const count = CountContext.unstable_read(); + const count = readContext(CountContext); return ; } Counter = pure(Counter); diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index fa9ff223f83c..71d1b3860544 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -11,25 +11,9 @@ import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type {ReactContext} from 'shared/ReactTypes'; -import invariant from 'shared/invariant'; import warningWithoutStack from 'shared/warningWithoutStack'; import warning from 'shared/warning'; -import ReactCurrentOwner from './ReactCurrentOwner'; - -export function readContext( - context: ReactContext, - observedBits: void | number | boolean, -): T { - const dispatcher = ReactCurrentOwner.currentDispatcher; - invariant( - dispatcher !== null, - 'Context.unstable_read(): Context can only be read while React is ' + - 'rendering, e.g. inside the render method or getDerivedStateFromProps.', - ); - return dispatcher.readContext(context, observedBits); -} - export function createContext( defaultValue: T, calculateChangedBits: ?(a: T, b: T) => number, @@ -61,7 +45,6 @@ export function createContext( // These are circular Provider: (null: any), Consumer: (null: any), - unstable_read: (null: any), }; context.Provider = { @@ -69,10 +52,7 @@ export function createContext( _context: context, }; - context.unstable_read = readContext.bind(null, context); - let hasWarnedAboutUsingNestedContextConsumers = false; - let hasWarnedAboutUsingConsumerUnstableRead = false; let hasWarnedAboutUsingConsumerProvider = false; if (__DEV__) { @@ -83,17 +63,6 @@ export function createContext( $$typeof: REACT_CONTEXT_TYPE, _context: context, _calculateChangedBits: context._calculateChangedBits, - unstable_read() { - if (!hasWarnedAboutUsingConsumerUnstableRead) { - hasWarnedAboutUsingConsumerUnstableRead = true; - warning( - false, - 'Calling Context.Consumer.unstable_read() is not supported and will be removed in ' + - 'a future major release. Did you mean to render Context.unstable_read() instead?', - ); - } - return context.unstable_read(); - }, }; // $FlowFixMe: Flow complains about not setting a value, which is intentional here Object.defineProperties(Consumer, { diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 20f76eef30f8..080ade12b807 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -54,7 +54,6 @@ export type ReactContext = { $$typeof: Symbol | number, Consumer: ReactContext, Provider: ReactProviderType, - unstable_read: () => T, _calculateChangedBits: ((a: T, b: T) => number) | null, From 35cf54cdc14a7ce78f1b3eb3468de60496e7bbfe Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 16 Oct 2018 14:29:11 -0400 Subject: [PATCH 2/2] This no longer throws immediately --- packages/react/src/__tests__/ReactContextValidator-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react/src/__tests__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js index 4ce63cc3d5c3..7ee787e2250d 100644 --- a/packages/react/src/__tests__/ReactContextValidator-test.js +++ b/packages/react/src/__tests__/ReactContextValidator-test.js @@ -556,7 +556,7 @@ describe('ReactContextValidator', () => { } expect(() => { - expect(() => ReactTestUtils.renderIntoDocument()).toThrow(); + ReactTestUtils.renderIntoDocument(); }).toWarnDev( 'Warning: ComponentA defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext(). ' + @@ -565,10 +565,10 @@ describe('ReactContextValidator', () => { ); // Warnings should be deduped by component type - expect(() => ReactTestUtils.renderIntoDocument()).toThrow(); + ReactTestUtils.renderIntoDocument(); expect(() => { - expect(() => ReactTestUtils.renderIntoDocument()).toThrow(); + ReactTestUtils.renderIntoDocument(); }).toWarnDev( 'Warning: ComponentB defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext(). ' +