diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js index 397b739546dcc..3242fdd3e81f4 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js @@ -736,17 +736,6 @@ describe('ReactDOMServerHooks', () => { }, ); - itRenders('warns when bitmask is passed to useContext', async render => { - const Context = React.createContext('Hi'); - - function Foo() { - return {useContext(Context, 1)}; - } - - const domNode = await render(, 1); - expect(domNode.textContent).toBe('Hi'); - }); - describe('useDebugValue', () => { itRenders('is a noop', async render => { function Counter(props) { @@ -760,11 +749,11 @@ describe('ReactDOMServerHooks', () => { }); describe('readContext', () => { - function readContext(Context, observedBits) { + function readContext(Context) { const dispatcher = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .ReactCurrentDispatcher.current; - return dispatcher.readContext(Context, observedBits); + return dispatcher.readContext(Context); } itRenders( diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 345f668fc62e8..dd88f1beb8fd1 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -160,7 +160,6 @@ import { checkIfContextChanged, readContext, prepareToReadContext, - calculateChangedBits, scheduleWorkOnParentPath, } from './ReactFiberNewContext.new'; import {renderWithHooks, bailoutHooks} from './ReactFiberHooks.new'; @@ -221,7 +220,7 @@ import { restoreSpawnedCachePool, getOffscreenDeferredCachePool, } from './ReactFiberCacheComponent.new'; -import {MAX_SIGNED_31_BIT_INT} from './MaxInts'; +import is from 'shared/objectIs'; import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev'; @@ -796,12 +795,7 @@ function updateCacheComponent( pushCacheProvider(workInProgress, nextCache); if (nextCache !== prevState.cache) { // This cache refreshed. Propagate a context change. - propagateContextChange( - workInProgress, - CacheContext, - MAX_SIGNED_31_BIT_INT, - renderLanes, - ); + propagateContextChange(workInProgress, CacheContext, renderLanes); } } } @@ -1168,12 +1162,7 @@ function updateHostRoot(current, workInProgress, renderLanes) { pushCacheProvider(workInProgress, nextCache); if (nextCache !== prevState.cache) { // The root cache refreshed. - propagateContextChange( - workInProgress, - CacheContext, - MAX_SIGNED_31_BIT_INT, - renderLanes, - ); + propagateContextChange(workInProgress, CacheContext, renderLanes); } } @@ -3007,8 +2996,7 @@ function updateContextProvider( } else { if (oldProps !== null) { const oldValue = oldProps.value; - const changedBits = calculateChangedBits(context, newValue, oldValue); - if (changedBits === 0) { + if (is(oldValue, newValue)) { // No change. Bailout early if children are the same. if ( oldProps.children === newProps.children && @@ -3023,12 +3011,7 @@ function updateContextProvider( } else { // The context value changed. Search for matching consumers and schedule // them to update. - propagateContextChange( - workInProgress, - context, - changedBits, - renderLanes, - ); + propagateContextChange(workInProgress, context, renderLanes); } } } @@ -3086,7 +3069,7 @@ function updateContextConsumer( } prepareToReadContext(workInProgress, renderLanes); - const newValue = readContext(context, newProps.unstable_observedBits); + const newValue = readContext(context); let newChildren; if (__DEV__) { ReactCurrentOwner.current = workInProgress; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 72b4e33431b00..1dd3ff43ffac1 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -160,7 +160,6 @@ import { checkIfContextChanged, readContext, prepareToReadContext, - calculateChangedBits, scheduleWorkOnParentPath, } from './ReactFiberNewContext.old'; import {renderWithHooks, bailoutHooks} from './ReactFiberHooks.old'; @@ -221,7 +220,7 @@ import { restoreSpawnedCachePool, getOffscreenDeferredCachePool, } from './ReactFiberCacheComponent.old'; -import {MAX_SIGNED_31_BIT_INT} from './MaxInts'; +import is from 'shared/objectIs'; import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev'; @@ -796,12 +795,7 @@ function updateCacheComponent( pushCacheProvider(workInProgress, nextCache); if (nextCache !== prevState.cache) { // This cache refreshed. Propagate a context change. - propagateContextChange( - workInProgress, - CacheContext, - MAX_SIGNED_31_BIT_INT, - renderLanes, - ); + propagateContextChange(workInProgress, CacheContext, renderLanes); } } } @@ -1168,12 +1162,7 @@ function updateHostRoot(current, workInProgress, renderLanes) { pushCacheProvider(workInProgress, nextCache); if (nextCache !== prevState.cache) { // The root cache refreshed. - propagateContextChange( - workInProgress, - CacheContext, - MAX_SIGNED_31_BIT_INT, - renderLanes, - ); + propagateContextChange(workInProgress, CacheContext, renderLanes); } } @@ -3007,8 +2996,7 @@ function updateContextProvider( } else { if (oldProps !== null) { const oldValue = oldProps.value; - const changedBits = calculateChangedBits(context, newValue, oldValue); - if (changedBits === 0) { + if (is(oldValue, newValue)) { // No change. Bailout early if children are the same. if ( oldProps.children === newProps.children && @@ -3023,12 +3011,7 @@ function updateContextProvider( } else { // The context value changed. Search for matching consumers and schedule // them to update. - propagateContextChange( - workInProgress, - context, - changedBits, - renderLanes, - ); + propagateContextChange(workInProgress, context, renderLanes); } } } @@ -3086,7 +3069,7 @@ function updateContextConsumer( } prepareToReadContext(workInProgress, renderLanes); - const newValue = readContext(context, newProps.unstable_observedBits); + const newValue = readContext(context); let newChildren; if (__DEV__) { ReactCurrentOwner.current = workInProgress; diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js index b5ae3ccc611a8..042a8b2efc098 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.new.js @@ -37,7 +37,6 @@ export const CacheContext: ReactContext = enableCache // We don't use Consumer/Provider for Cache components. So we'll cheat. Consumer: (null: any), Provider: (null: any), - _calculateChangedBits: null, // We'll initialize these at the root. _currentValue: (null: any), _currentValue2: (null: any), diff --git a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js index 8882f7dbd2d48..2bc64254d3092 100644 --- a/packages/react-reconciler/src/ReactFiberCacheComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberCacheComponent.old.js @@ -37,7 +37,6 @@ export const CacheContext: ReactContext = enableCache // We don't use Consumer/Provider for Cache components. So we'll cheat. Consumer: (null: any), Provider: (null: any), - _calculateChangedBits: null, // We'll initialize these at the root. _currentValue: (null: any), _currentValue2: (null: any), diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index e7e5c3128beac..3c1c6c43081fc 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2238,11 +2238,8 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2250,13 +2247,10 @@ if (__DEV__) { checkDepsAreArrayDev(deps); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; mountHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2373,24 +2367,18 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; updateHookTypesDev(); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2503,24 +2491,18 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2633,11 +2615,8 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2645,13 +2624,10 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2764,12 +2740,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2777,14 +2750,11 @@ if (__DEV__) { mountHookTypesDev(); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2909,12 +2879,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2922,14 +2889,11 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -3054,12 +3018,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -3068,14 +3029,11 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 55dca75656c35..2a0f70e1913b6 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2238,11 +2238,8 @@ if (__DEV__) { }; HooksDispatcherOnMountInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2250,13 +2247,10 @@ if (__DEV__) { checkDepsAreArrayDev(deps); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; mountHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2373,24 +2367,18 @@ if (__DEV__) { } HooksDispatcherOnMountWithHookTypesInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; updateHookTypesDev(); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2503,24 +2491,18 @@ if (__DEV__) { } HooksDispatcherOnUpdateInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2633,11 +2615,8 @@ if (__DEV__) { } HooksDispatcherOnRerenderInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { - return readContext(context, observedBits); + readContext(context: ReactContext): T { + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -2645,13 +2624,10 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2764,12 +2740,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnMountInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2777,14 +2750,11 @@ if (__DEV__) { mountHookTypesDev(); return mountCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); mountHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -2909,12 +2879,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { currentHookNameInDev = 'useCallback'; @@ -2922,14 +2889,11 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, @@ -3054,12 +3018,9 @@ if (__DEV__) { } InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + readContext(context: ReactContext): T { warnInvalidContextAccess(); - return readContext(context, observedBits); + return readContext(context); }, useCallback(callback: T, deps: Array | void | null): T { @@ -3068,14 +3029,11 @@ if (__DEV__) { updateHookTypesDev(); return updateCallback(callback, deps); }, - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T { + useContext(context: ReactContext): T { currentHookNameInDev = 'useContext'; warnInvalidHookAccess(); updateHookTypesDev(); - return readContext(context, observedBits); + return readContext(context); }, useEffect( create: () => (() => void) | void, diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index 29a1eeef8cdd9..9d2484a783971 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -19,7 +19,6 @@ import type {SharedQueue} from './ReactUpdateQueue.new'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.new'; -import {MAX_SIGNED_31_BIT_INT} from './MaxInts'; import { ContextProvider, ClassComponent, @@ -58,7 +57,7 @@ if (__DEV__) { let currentlyRenderingFiber: Fiber | null = null; let lastContextDependency: ContextDependency | null = null; -let lastContextWithAllBitsObserved: ReactContext | null = null; +let lastFullyObservedContext: ReactContext | null = null; let isDisallowedContextReadInDEV: boolean = false; @@ -67,7 +66,7 @@ export function resetContextDependencies(): void { // cannot be called outside the render phase. currentlyRenderingFiber = null; lastContextDependency = null; - lastContextWithAllBitsObserved = null; + lastFullyObservedContext = null; if (__DEV__) { isDisallowedContextReadInDEV = false; } @@ -140,33 +139,6 @@ export function popProvider( } } -export function calculateChangedBits( - context: ReactContext, - newValue: T, - oldValue: T, -) { - if (is(oldValue, newValue)) { - // No change - return 0; - } else { - const changedBits = - typeof context._calculateChangedBits === 'function' - ? context._calculateChangedBits(oldValue, newValue) - : MAX_SIGNED_31_BIT_INT; - - if (__DEV__) { - if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) { - console.error( - 'calculateChangedBits: Expected the return value to be a ' + - '31-bit integer. Instead received: %s', - changedBits, - ); - } - } - return changedBits | 0; - } -} - export function scheduleWorkOnParentPath( parent: Fiber | null, renderLanes: Lanes, @@ -197,7 +169,6 @@ export function scheduleWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, context: ReactContext, - changedBits: number, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -207,24 +178,18 @@ export function propagateContextChange( const forcePropagateEntireTree = true; propagateContextChanges( workInProgress, - [context, changedBits], + [context], renderLanes, forcePropagateEntireTree, ); } else { - propagateContextChange_eager( - workInProgress, - context, - changedBits, - renderLanes, - ); + propagateContextChange_eager(workInProgress, context, renderLanes); } } function propagateContextChange_eager( workInProgress: Fiber, context: ReactContext, - changedBits: number, renderLanes: Lanes, ): void { // Only used by eager implemenation @@ -247,10 +212,7 @@ function propagateContextChange_eager( let dependency = list.firstContext; while (dependency !== null) { // Check if the context matches. - if ( - dependency.context === context && - (dependency.observedBits & changedBits) !== 0 - ) { + if (dependency.context === context) { // Match! Schedule an update on this fiber. if (fiber.tag === ClassComponent) { // Schedule a force update on the work-in-progress. @@ -382,15 +344,11 @@ function propagateContextChanges( // Assigning these to constants to help Flow const dependency = dep; const consumer = fiber; - findContext: for (let i = 0; i < contexts.length; i += 2) { + findContext: for (let i = 0; i < contexts.length; i++) { const context: ReactContext = contexts[i]; - const changedBits: number = contexts[i + 1]; // Check if the context matches. // TODO: Compare selected values to bail out early. - if ( - dependency.context === context && - (dependency.observedBits & changedBits) !== 0 - ) { + if (dependency.context === context) { // Match! Schedule an update on this fiber. // In the lazy implemenation, don't mark a dirty flag on the @@ -549,12 +507,11 @@ function propagateParentContextChanges( const oldValue = oldProps.value; - const changedBits = calculateChangedBits(context, newValue, oldValue); - if (changedBits !== 0) { + if (!is(newValue, oldValue)) { if (contexts !== null) { - contexts.push(context, changedBits); + contexts.push(context); } else { - contexts = [context, changedBits]; + contexts = [context]; } } } @@ -625,7 +582,7 @@ export function prepareToReadContext( ): void { currentlyRenderingFiber = workInProgress; lastContextDependency = null; - lastContextWithAllBitsObserved = null; + lastFullyObservedContext = null; const dependencies = workInProgress.dependencies; if (dependencies !== null) { @@ -646,10 +603,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext, - observedBits: void | number | boolean, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. @@ -667,26 +621,11 @@ export function readContext( ? context._currentValue : context._currentValue2; - if (lastContextWithAllBitsObserved === context) { + if (lastFullyObservedContext === context) { // Nothing to do. We already observe everything in this context. - } else if (observedBits === false || observedBits === 0) { - // Do not observe any updates. } else { - let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types. - if ( - typeof observedBits !== 'number' || - observedBits === MAX_SIGNED_31_BIT_INT - ) { - // Observe all updates. - lastContextWithAllBitsObserved = ((context: any): ReactContext); - resolvedObservedBits = MAX_SIGNED_31_BIT_INT; - } else { - resolvedObservedBits = observedBits; - } - const contextItem = { context: ((context: any): ReactContext), - observedBits: resolvedObservedBits, memoizedValue: value, next: null, }; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index 0e6ed587b8ddb..c2a1600650119 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -19,7 +19,6 @@ import type {SharedQueue} from './ReactUpdateQueue.old'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.old'; -import {MAX_SIGNED_31_BIT_INT} from './MaxInts'; import { ContextProvider, ClassComponent, @@ -58,7 +57,7 @@ if (__DEV__) { let currentlyRenderingFiber: Fiber | null = null; let lastContextDependency: ContextDependency | null = null; -let lastContextWithAllBitsObserved: ReactContext | null = null; +let lastFullyObservedContext: ReactContext | null = null; let isDisallowedContextReadInDEV: boolean = false; @@ -67,7 +66,7 @@ export function resetContextDependencies(): void { // cannot be called outside the render phase. currentlyRenderingFiber = null; lastContextDependency = null; - lastContextWithAllBitsObserved = null; + lastFullyObservedContext = null; if (__DEV__) { isDisallowedContextReadInDEV = false; } @@ -140,33 +139,6 @@ export function popProvider( } } -export function calculateChangedBits( - context: ReactContext, - newValue: T, - oldValue: T, -) { - if (is(oldValue, newValue)) { - // No change - return 0; - } else { - const changedBits = - typeof context._calculateChangedBits === 'function' - ? context._calculateChangedBits(oldValue, newValue) - : MAX_SIGNED_31_BIT_INT; - - if (__DEV__) { - if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) { - console.error( - 'calculateChangedBits: Expected the return value to be a ' + - '31-bit integer. Instead received: %s', - changedBits, - ); - } - } - return changedBits | 0; - } -} - export function scheduleWorkOnParentPath( parent: Fiber | null, renderLanes: Lanes, @@ -197,7 +169,6 @@ export function scheduleWorkOnParentPath( export function propagateContextChange( workInProgress: Fiber, context: ReactContext, - changedBits: number, renderLanes: Lanes, ): void { if (enableLazyContextPropagation) { @@ -207,24 +178,18 @@ export function propagateContextChange( const forcePropagateEntireTree = true; propagateContextChanges( workInProgress, - [context, changedBits], + [context], renderLanes, forcePropagateEntireTree, ); } else { - propagateContextChange_eager( - workInProgress, - context, - changedBits, - renderLanes, - ); + propagateContextChange_eager(workInProgress, context, renderLanes); } } function propagateContextChange_eager( workInProgress: Fiber, context: ReactContext, - changedBits: number, renderLanes: Lanes, ): void { // Only used by eager implemenation @@ -247,10 +212,7 @@ function propagateContextChange_eager( let dependency = list.firstContext; while (dependency !== null) { // Check if the context matches. - if ( - dependency.context === context && - (dependency.observedBits & changedBits) !== 0 - ) { + if (dependency.context === context) { // Match! Schedule an update on this fiber. if (fiber.tag === ClassComponent) { // Schedule a force update on the work-in-progress. @@ -382,15 +344,11 @@ function propagateContextChanges( // Assigning these to constants to help Flow const dependency = dep; const consumer = fiber; - findContext: for (let i = 0; i < contexts.length; i += 2) { + findContext: for (let i = 0; i < contexts.length; i++) { const context: ReactContext = contexts[i]; - const changedBits: number = contexts[i + 1]; // Check if the context matches. // TODO: Compare selected values to bail out early. - if ( - dependency.context === context && - (dependency.observedBits & changedBits) !== 0 - ) { + if (dependency.context === context) { // Match! Schedule an update on this fiber. // In the lazy implemenation, don't mark a dirty flag on the @@ -549,12 +507,11 @@ function propagateParentContextChanges( const oldValue = oldProps.value; - const changedBits = calculateChangedBits(context, newValue, oldValue); - if (changedBits !== 0) { + if (!is(newValue, oldValue)) { if (contexts !== null) { - contexts.push(context, changedBits); + contexts.push(context); } else { - contexts = [context, changedBits]; + contexts = [context]; } } } @@ -625,7 +582,7 @@ export function prepareToReadContext( ): void { currentlyRenderingFiber = workInProgress; lastContextDependency = null; - lastContextWithAllBitsObserved = null; + lastFullyObservedContext = null; const dependencies = workInProgress.dependencies; if (dependencies !== null) { @@ -646,10 +603,7 @@ export function prepareToReadContext( } } -export function readContext( - context: ReactContext, - observedBits: void | number | boolean, -): T { +export function readContext(context: ReactContext): T { if (__DEV__) { // This warning would fire if you read context inside a Hook like useMemo. // Unlike the class check below, it's not enforced in production for perf. @@ -667,26 +621,11 @@ export function readContext( ? context._currentValue : context._currentValue2; - if (lastContextWithAllBitsObserved === context) { + if (lastFullyObservedContext === context) { // Nothing to do. We already observe everything in this context. - } else if (observedBits === false || observedBits === 0) { - // Do not observe any updates. } else { - let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types. - if ( - typeof observedBits !== 'number' || - observedBits === MAX_SIGNED_31_BIT_INT - ) { - // Observe all updates. - lastContextWithAllBitsObserved = ((context: any): ReactContext); - resolvedObservedBits = MAX_SIGNED_31_BIT_INT; - } else { - resolvedObservedBits = observedBits; - } - const contextItem = { context: ((context: any): ReactContext), - observedBits: resolvedObservedBits, memoizedValue: value, next: null, }; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index b1764bed5ac34..e5fd43e6f416e 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -49,7 +49,6 @@ export type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90; export type ContextDependency = { context: ReactContext, - observedBits: number, next: ContextDependency | null, memoizedValue: T, ... @@ -281,20 +280,14 @@ type Dispatch = A => void; export type Dispatcher = {| getCacheForType?: (resourceType: () => T) => T, - readContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T, + readContext(context: ReactContext): T, useState(initialState: (() => S) | S): [S, Dispatch>], useReducer( reducer: (S, A) => S, initialArg: I, init?: (I) => S, ): [S, Dispatch], - useContext( - context: ReactContext, - observedBits: void | number | boolean, - ): T, + useContext(context: ReactContext): T, useRef(initialValue: T): {|current: T|}, useEffect( create: () => (() => void) | void, diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js index 43c1e0033c2f3..4f64972715ac4 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js @@ -35,11 +35,11 @@ describe('ReactNewContext', () => { return {type: 'span', children: [], prop, hidden: false}; } - function readContext(Context, observedBits) { + function readContext(Context) { const dispatcher = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .ReactCurrentDispatcher.current; - return dispatcher.readContext(Context, observedBits); + return dispatcher.readContext(Context); } // Note: This is based on a similar component we use in www. We can delete @@ -62,48 +62,21 @@ describe('ReactNewContext', () => { 'useContext inside function component', Context => function Consumer(props) { - const observedBits = props.unstable_observedBits; - let contextValue; - expect(() => { - contextValue = useContext(Context, observedBits); - }).toErrorDev( - observedBits !== undefined - ? 'useContext() second argument is reserved for future use in React. ' + - `Passing it is not supported. You passed: ${observedBits}.` - : [], - ); + const contextValue = useContext(Context); const render = props.children; return render(contextValue); }, ); sharedContextTests('useContext inside forwardRef component', Context => React.forwardRef(function Consumer(props, ref) { - const observedBits = props.unstable_observedBits; - let contextValue; - expect(() => { - contextValue = useContext(Context, observedBits); - }).toErrorDev( - observedBits !== undefined - ? 'useContext() second argument is reserved for future use in React. ' + - `Passing it is not supported. You passed: ${observedBits}.` - : [], - ); + const contextValue = useContext(Context); const render = props.children; return render(contextValue); }), ); sharedContextTests('useContext inside memoized function component', Context => React.memo(function Consumer(props) { - const observedBits = props.unstable_observedBits; - let contextValue; - expect(() => { - contextValue = useContext(Context, observedBits); - }).toErrorDev( - observedBits !== undefined - ? 'useContext() second argument is reserved for future use in React. ' + - `Passing it is not supported. You passed: ${observedBits}.` - : [], - ); + const contextValue = useContext(Context); const render = props.children; return render(contextValue); }), @@ -113,8 +86,7 @@ describe('ReactNewContext', () => { Context => class Consumer extends React.Component { render() { - const observedBits = this.props.unstable_observedBits; - const contextValue = readContext(Context, observedBits); + const contextValue = readContext(Context); const render = this.props.children; return render(contextValue); } @@ -125,8 +97,7 @@ describe('ReactNewContext', () => { Context => class Consumer extends React.PureComponent { render() { - const observedBits = this.props.unstable_observedBits; - const contextValue = readContext(Context, observedBits); + const contextValue = readContext(Context); const render = this.props.children; return render(contextValue); } @@ -590,227 +561,6 @@ describe('ReactNewContext', () => { ]); }); - it('can skip consumers with bitmask', () => { - const Context = React.createContext({foo: 0, bar: 0}, (a, b) => { - let result = 0; - if (a.foo !== b.foo) { - result |= 0b01; - } - if (a.bar !== b.bar) { - result |= 0b10; - } - return result; - }); - const Consumer = getConsumer(Context); - - function Provider(props) { - return ( - - {props.children} - - ); - } - - function Foo() { - return ( - - {value => { - Scheduler.unstable_yieldValue('Foo'); - return ; - }} - - ); - } - - function Bar() { - return ( - - {value => { - Scheduler.unstable_yieldValue('Bar'); - return ; - }} - - ); - } - - class Indirection extends React.Component { - shouldComponentUpdate() { - return false; - } - render() { - return this.props.children; - } - } - - function App(props) { - return ( - - - - - - - - - - - ); - } - - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo', 'Bar']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 1'), - span('Bar: 1'), - ]); - - // Update only foo - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 2'), - span('Bar: 1'), - ]); - - // Update only bar - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Bar']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 2'), - span('Bar: 2'), - ]); - - // Update both - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo', 'Bar']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 3'), - span('Bar: 3'), - ]); - }); - - // @gate !enableLazyContextPropagation - it('can skip parents with bitmask bailout while updating their children', () => { - const Context = React.createContext({foo: 0, bar: 0}, (a, b) => { - let result = 0; - if (a.foo !== b.foo) { - result |= 0b01; - } - if (a.bar !== b.bar) { - result |= 0b10; - } - return result; - }); - const Consumer = getConsumer(Context); - - function Provider(props) { - return ( - - {props.children} - - ); - } - - function Foo(props) { - return ( - - {value => { - Scheduler.unstable_yieldValue('Foo'); - return ( - <> - - {props.children && props.children()} - - ); - }} - - ); - } - - function Bar(props) { - return ( - - {value => { - Scheduler.unstable_yieldValue('Bar'); - return ( - <> - - {props.children && props.children()} - - ); - }} - - ); - } - - class Indirection extends React.Component { - shouldComponentUpdate() { - return false; - } - render() { - return this.props.children; - } - } - - function App(props) { - return ( - - - - {/* Use a render prop so we don't test constant elements. */} - {() => ( - - - {() => ( - - - - )} - - - )} - - - - ); - } - - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Foo']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 1'), - span('Bar: 1'), - span('Foo: 1'), - ]); - - // Update only foo - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo', 'Foo']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 2'), - span('Bar: 1'), - span('Foo: 2'), - ]); - - // Update only bar - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Bar']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 2'), - span('Bar: 2'), - span('Foo: 2'), - ]); - - // Update both - ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Foo']); - expect(ReactNoop.getChildren()).toEqual([ - span('Foo: 3'), - span('Bar: 3'), - span('Foo: 3'), - ]); - }); - it("does not re-render if there's an update in a child", () => { const Context = React.createContext(0); const Consumer = getConsumer(Context); @@ -1063,30 +813,6 @@ describe('ReactNewContext', () => { } describe('Context.Provider', () => { - it('warns if calculateChangedBits returns larger than a 31-bit integer', () => { - const Context = React.createContext( - 0, - (a, b) => Math.pow(2, 32) - 1, // Return 32 bit int - ); - - function App(props) { - return ; - } - - ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); - - // Update - ReactNoop.render(); - - if (gate(flags => !flags.enableLazyContextPropagation)) { - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'calculateChangedBits: Expected the return value to be a 31-bit ' + - 'integer. Instead received: 4294967295', - ); - } - }); - it('warns if no value prop provided', () => { const Context = React.createContext(); @@ -1355,6 +1081,9 @@ describe('ReactNewContext', () => { }); describe('readContext', () => { + // Unstable changedBits API was removed. Port this test to context selectors + // once that exists. + // @gate FIXME it('can read the same context multiple times in the same function', () => { const Context = React.createContext({foo: 0, bar: 0, baz: 0}, (a, b) => { let result = 0; @@ -1517,22 +1246,6 @@ describe('ReactNewContext', () => { }); describe('useContext', () => { - it('warns on array.map(useContext)', () => { - const Context = React.createContext(0); - function Foo() { - return [Context].map(useContext); - } - ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( - 'useContext() second argument is reserved for future ' + - 'use in React. Passing it is not supported. ' + - 'You passed: 0.\n\n' + - 'Did you call array.map(useContext)? ' + - 'Calling Hooks inside a loop is not supported. ' + - 'Learn more at https://reactjs.org/link/rules-of-hooks', - ); - }); - it('throws when used in a class component', () => { const Context = React.createContext(0); class Foo extends React.Component { diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index a3c4243dd0116..41065c13ef067 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -11,30 +11,12 @@ import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import type {ReactContext} from 'shared/ReactTypes'; -export function createContext( - defaultValue: T, - calculateChangedBits: ?(a: T, b: T) => number, -): ReactContext { - if (calculateChangedBits === undefined) { - calculateChangedBits = null; - } else { - if (__DEV__) { - if ( - calculateChangedBits !== null && - typeof calculateChangedBits !== 'function' - ) { - console.error( - 'createContext: Expected the optional second argument to be a ' + - 'function. Instead received: %s', - calculateChangedBits, - ); - } - } - } +export function createContext(defaultValue: T): ReactContext { + // TODO: Second argument used to be an optional `calculateChangedBits` + // function. Warn to reserve for future use? const context: ReactContext = { $$typeof: REACT_CONTEXT_TYPE, - _calculateChangedBits: calculateChangedBits, // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. We only expect // there to be two concurrent renderers at most: React Native (primary) and @@ -66,7 +48,6 @@ export function createContext( const Consumer = { $$typeof: REACT_CONTEXT_TYPE, _context: context, - _calculateChangedBits: context._calculateChangedBits, }; // $FlowFixMe: Flow complains about not setting a value, which is intentional here Object.defineProperties(Consumer, { diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index d397d8f789f0a..18920be2c8df4 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -47,26 +47,9 @@ export function getCacheForType(resourceType: () => T): T { return dispatcher.getCacheForType(resourceType); } -export function useContext( - Context: ReactContext, - unstable_observedBits: number | boolean | void, -): T { +export function useContext(Context: ReactContext): T { const dispatcher = resolveDispatcher(); if (__DEV__) { - if (unstable_observedBits !== undefined) { - console.error( - 'useContext() second argument is reserved for future ' + - 'use in React. Passing it is not supported. ' + - 'You passed: %s.%s', - unstable_observedBits, - typeof unstable_observedBits === 'number' && Array.isArray(arguments[2]) - ? '\n\nDid you call array.map(useContext)? ' + - 'Calling Hooks inside a loop is not supported. ' + - 'Learn more at https://reactjs.org/link/rules-of-hooks' - : '', - ); - } - // TODO: add a more generic warning for invalid values. if ((Context: any)._context !== undefined) { const realContext = (Context: any)._context; @@ -85,7 +68,7 @@ export function useContext( } } } - return dispatcher.useContext(Context, unstable_observedBits); + return dispatcher.useContext(Context); } export function useState( diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index dc31b998ea31c..55061fe8b9b82 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -59,7 +59,6 @@ export type ReactContext = { $$typeof: Symbol | number, Consumer: ReactContext, Provider: ReactProviderType, - _calculateChangedBits: ((a: T, b: T) => number) | null, _currentValue: T, _currentValue2: T, _threadCount: number,