@samitouri / QOS-React-2 / commits / 6d3ecb70dc

Remove unstable_changedBits (#20953)

We added this unstable feature a few years ago, as a way to opt out of context updates, but it didn't prove useful in practice. We have other proposals for how to address the same problem, like context selectors. Since it was prefixed with `unstable_`, we should be able to remove it without consequence. The hook API already warned if you used it. Even if someone is using it somewhere, it's meant to be an optimization only, so if they are using the API properly, it should not have any semantic impact.

Andrew Clark committed Mar 19, 2021 at 17:36 UTC 6d3ecb70dceb225af0cd990b46d6c44b852c1d82
19 files changed +123 -720
packages/react-cache/src/ReactCacheOld.js
+2 -2
@@ -46,7 +46,7 @@ const ReactCurrentDispatcher =
46 React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
47 .ReactCurrentDispatcher;
48
49 -function readContext(Context, observedBits) {
49 +function readContext(Context) {
50 const dispatcher = ReactCurrentDispatcher.current;
51 if (dispatcher === null) {
52 throw new Error(
@@ -55,7 +55,7 @@ function readContext(Context, observedBits) {
55 'lifecycle methods.',
56 );
57 }
58 - return dispatcher.readContext(Context, observedBits);
58 + return dispatcher.readContext(Context);
59 }
60
61 function identityHashFn(input) {
packages/react-debug-tools/src/ReactDebugHooks.js
+2 -8
@@ -109,18 +109,12 @@ function getCacheForType<T>(resourceType: () => T): T {
109 invariant(false, 'Not implemented.');
110 }
111
112 -function readContext<T>(
113 - context: ReactContext<T>,
114 - observedBits: void | number | boolean,
115 -): T {
112 +function readContext<T>(context: ReactContext<T>): T {
113 // For now we don't expose readContext usage in the hooks debugging info.
114 return context._currentValue;
115 }
116
120 -function useContext<T>(
121 - context: ReactContext<T>,
122 - observedBits: void | number | boolean,
123 -): T {
117 +function useContext<T>(context: ReactContext<T>): T {
118 hookLog.push({
119 primitive: 'Context',
120 stackError: new Error(),
packages/react-devtools-shared/src/devtools/cache.js
+2 -2
@@ -61,7 +61,7 @@ const Rejected = 2;
61 const ReactCurrentDispatcher = (React: any)
62 .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;
63
64 -function readContext(Context, observedBits) {
64 +function readContext(Context) {
65 const dispatcher = ReactCurrentDispatcher.current;
66 if (dispatcher === null) {
67 throw new Error(
@@ -70,7 +70,7 @@ function readContext(Context, observedBits) {
70 'lifecycle methods.',
71 );
72 }
73 - return dispatcher.readContext(Context, observedBits);
73 + return dispatcher.readContext(Context);
74 }
75
76 const CacheContext = createContext(null);
packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
+2 -13
@@ -736,17 +736,6 @@ describe('ReactDOMServerHooks', () => {
736 },
737 );
738
739 - itRenders('warns when bitmask is passed to useContext', async render => {
740 - const Context = React.createContext('Hi');
741 -
742 - function Foo() {
743 - return <span>{useContext(Context, 1)}</span>;
744 - }
745 -
746 - const domNode = await render(<Foo />, 1);
747 - expect(domNode.textContent).toBe('Hi');
748 - });
749 -
739 describe('useDebugValue', () => {
740 itRenders('is a noop', async render => {
741 function Counter(props) {
@@ -760,11 +749,11 @@ describe('ReactDOMServerHooks', () => {
749 });
750
751 describe('readContext', () => {
763 - function readContext(Context, observedBits) {
752 + function readContext(Context) {
753 const dispatcher =
754 React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
755 .ReactCurrentDispatcher.current;
767 - return dispatcher.readContext(Context, observedBits);
756 + return dispatcher.readContext(Context);
757 }
758
759 itRenders(
packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
+2 -2
@@ -161,11 +161,11 @@ describe('ReactDOMServerIntegration', () => {
161 });
162
163 itRenders('readContext() in different components', async render => {
164 - function readContext(Ctx, observedBits) {
164 + function readContext(Ctx) {
165 const dispatcher =
166 React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
167 .ReactCurrentDispatcher.current;
168 - return dispatcher.readContext(Ctx, observedBits);
168 + return dispatcher.readContext(Ctx);
169 }
170
171 class Cls extends React.Component {
packages/react-dom/src/server/ReactPartialRendererHooks.js
+2 -8
@@ -219,10 +219,7 @@ function getCacheForType<T>(resourceType: () => T): T {
219 invariant(false, 'Not implemented.');
220 }
221
222 -function readContext<T>(
223 - context: ReactContext<T>,
224 - observedBits: void | number | boolean,
225 -): T {
222 +function readContext<T>(context: ReactContext<T>): T {
223 const threadID = currentPartialRenderer.threadID;
224 validateContextBounds(context, threadID);
225 if (__DEV__) {
@@ -238,10 +235,7 @@ function readContext<T>(
235 return context[threadID];
236 }
237
241 -function useContext<T>(
242 - context: ReactContext<T>,
243 - observedBits: void | number | boolean,
244 -): T {
238 +function useContext<T>(context: ReactContext<T>): T {
239 if (__DEV__) {
240 currentHookNameInDev = 'useContext';
241 }
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+6 -23
@@ -160,7 +160,6 @@ import {
160 checkIfContextChanged,
161 readContext,
162 prepareToReadContext,
163 - calculateChangedBits,
163 scheduleWorkOnParentPath,
164 } from './ReactFiberNewContext.new';
165 import {renderWithHooks, bailoutHooks} from './ReactFiberHooks.new';
@@ -221,7 +220,7 @@ import {
220 restoreSpawnedCachePool,
221 getOffscreenDeferredCachePool,
222 } from './ReactFiberCacheComponent.new';
224 -import {MAX_SIGNED_31_BIT_INT} from './MaxInts';
223 +import is from 'shared/objectIs';
224
225 import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
226
@@ -795,12 +794,7 @@ function updateCacheComponent(
794 pushCacheProvider(workInProgress, nextCache);
795 if (nextCache !== prevState.cache) {
796 // This cache refreshed. Propagate a context change.
798 - propagateContextChange(
799 - workInProgress,
800 - CacheContext,
801 - MAX_SIGNED_31_BIT_INT,
802 - renderLanes,
803 - );
797 + propagateContextChange(workInProgress, CacheContext, renderLanes);
798 }
799 }
800 }
@@ -1169,12 +1163,7 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1163 pushCacheProvider(workInProgress, nextCache);
1164 if (nextCache !== prevState.cache) {
1165 // The root cache refreshed.
1172 - propagateContextChange(
1173 - workInProgress,
1174 - CacheContext,
1175 - MAX_SIGNED_31_BIT_INT,
1176 - renderLanes,
1177 - );
1166 + propagateContextChange(workInProgress, CacheContext, renderLanes);
1167 }
1168 }
1169
@@ -3011,8 +3000,7 @@ function updateContextProvider(
3000 } else {
3001 if (oldProps !== null) {
3002 const oldValue = oldProps.value;
3014 - const changedBits = calculateChangedBits(context, newValue, oldValue);
3015 - if (changedBits === 0) {
3003 + if (is(oldValue, newValue)) {
3004 // No change. Bailout early if children are the same.
3005 if (
3006 oldProps.children === newProps.children &&
@@ -3027,12 +3015,7 @@ function updateContextProvider(
3015 } else {
3016 // The context value changed. Search for matching consumers and schedule
3017 // them to update.
3030 - propagateContextChange(
3031 - workInProgress,
3032 - context,
3033 - changedBits,
3034 - renderLanes,
3035 - );
3018 + propagateContextChange(workInProgress, context, renderLanes);
3019 }
3020 }
3021 }
@@ -3090,7 +3073,7 @@ function updateContextConsumer(
3073 }
3074
3075 prepareToReadContext(workInProgress, renderLanes);
3093 - const newValue = readContext(context, newProps.unstable_observedBits);
3076 + const newValue = readContext(context);
3077 let newChildren;
3078 if (__DEV__) {
3079 ReactCurrentOwner.current = workInProgress;
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+6 -23
@@ -160,7 +160,6 @@ import {
160 checkIfContextChanged,
161 readContext,
162 prepareToReadContext,
163 - calculateChangedBits,
163 scheduleWorkOnParentPath,
164 } from './ReactFiberNewContext.old';
165 import {renderWithHooks, bailoutHooks} from './ReactFiberHooks.old';
@@ -221,7 +220,7 @@ import {
220 restoreSpawnedCachePool,
221 getOffscreenDeferredCachePool,
222 } from './ReactFiberCacheComponent.old';
224 -import {MAX_SIGNED_31_BIT_INT} from './MaxInts';
223 +import is from 'shared/objectIs';
224
225 import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
226
@@ -795,12 +794,7 @@ function updateCacheComponent(
794 pushCacheProvider(workInProgress, nextCache);
795 if (nextCache !== prevState.cache) {
796 // This cache refreshed. Propagate a context change.
798 - propagateContextChange(
799 - workInProgress,
800 - CacheContext,
801 - MAX_SIGNED_31_BIT_INT,
802 - renderLanes,
803 - );
797 + propagateContextChange(workInProgress, CacheContext, renderLanes);
798 }
799 }
800 }
@@ -1169,12 +1163,7 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1163 pushCacheProvider(workInProgress, nextCache);
1164 if (nextCache !== prevState.cache) {
1165 // The root cache refreshed.
1172 - propagateContextChange(
1173 - workInProgress,
1174 - CacheContext,
1175 - MAX_SIGNED_31_BIT_INT,
1176 - renderLanes,
1177 - );
1166 + propagateContextChange(workInProgress, CacheContext, renderLanes);
1167 }
1168 }
1169
@@ -3011,8 +3000,7 @@ function updateContextProvider(
3000 } else {
3001 if (oldProps !== null) {
3002 const oldValue = oldProps.value;
3014 - const changedBits = calculateChangedBits(context, newValue, oldValue);
3015 - if (changedBits === 0) {
3003 + if (is(oldValue, newValue)) {
3004 // No change. Bailout early if children are the same.
3005 if (
3006 oldProps.children === newProps.children &&
@@ -3027,12 +3015,7 @@ function updateContextProvider(
3015 } else {
3016 // The context value changed. Search for matching consumers and schedule
3017 // them to update.
3030 - propagateContextChange(
3031 - workInProgress,
3032 - context,
3033 - changedBits,
3034 - renderLanes,
3035 - );
3018 + propagateContextChange(workInProgress, context, renderLanes);
3019 }
3020 }
3021 }
@@ -3090,7 +3073,7 @@ function updateContextConsumer(
3073 }
3074
3075 prepareToReadContext(workInProgress, renderLanes);
3093 - const newValue = readContext(context, newProps.unstable_observedBits);
3076 + const newValue = readContext(context);
3077 let newChildren;
3078 if (__DEV__) {
3079 ReactCurrentOwner.current = workInProgress;
packages/react-reconciler/src/ReactFiberCacheComponent.new.js
-1
@@ -37,7 +37,6 @@ export const CacheContext: ReactContext<Cache> = enableCache
37 // We don't use Consumer/Provider for Cache components. So we'll cheat.
38 Consumer: (null: any),
39 Provider: (null: any),
40 - _calculateChangedBits: null,
40 // We'll initialize these at the root.
41 _currentValue: (null: any),
42 _currentValue2: (null: any),
packages/react-reconciler/src/ReactFiberCacheComponent.old.js
-1
@@ -37,7 +37,6 @@ export const CacheContext: ReactContext<Cache> = enableCache
37 // We don't use Consumer/Provider for Cache components. So we'll cheat.
38 Consumer: (null: any),
39 Provider: (null: any),
40 - _calculateChangedBits: null,
40 // We'll initialize these at the root.
41 _currentValue: (null: any),
42 _currentValue2: (null: any),
packages/react-reconciler/src/ReactFiberHooks.new.js
+28 -70
@@ -2191,11 +2191,8 @@ if (__DEV__) {
2191 };
2192
2193 HooksDispatcherOnMountInDEV = {
2194 - readContext<T>(
2195 - context: ReactContext<T>,
2196 - observedBits: void | number | boolean,
2197 - ): T {
2198 - return readContext(context, observedBits);
2194 + readContext<T>(context: ReactContext<T>): T {
2195 + return readContext(context);
2196 },
2197 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2198 currentHookNameInDev = 'useCallback';
@@ -2203,13 +2200,10 @@ if (__DEV__) {
2200 checkDepsAreArrayDev(deps);
2201 return mountCallback(callback, deps);
2202 },
2206 - useContext<T>(
2207 - context: ReactContext<T>,
2208 - observedBits: void | number | boolean,
2209 - ): T {
2203 + useContext<T>(context: ReactContext<T>): T {
2204 currentHookNameInDev = 'useContext';
2205 mountHookTypesDev();
2212 - return readContext(context, observedBits);
2206 + return readContext(context);
2207 },
2208 useEffect(
2209 create: () => (() => void) | void,
@@ -2326,24 +2320,18 @@ if (__DEV__) {
2320 }
2321
2322 HooksDispatcherOnMountWithHookTypesInDEV = {
2329 - readContext<T>(
2330 - context: ReactContext<T>,
2331 - observedBits: void | number | boolean,
2332 - ): T {
2333 - return readContext(context, observedBits);
2323 + readContext<T>(context: ReactContext<T>): T {
2324 + return readContext(context);
2325 },
2326 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2327 currentHookNameInDev = 'useCallback';
2328 updateHookTypesDev();
2329 return mountCallback(callback, deps);
2330 },
2340 - useContext<T>(
2341 - context: ReactContext<T>,
2342 - observedBits: void | number | boolean,
2343 - ): T {
2331 + useContext<T>(context: ReactContext<T>): T {
2332 currentHookNameInDev = 'useContext';
2333 updateHookTypesDev();
2346 - return readContext(context, observedBits);
2334 + return readContext(context);
2335 },
2336 useEffect(
2337 create: () => (() => void) | void,
@@ -2456,24 +2444,18 @@ if (__DEV__) {
2444 }
2445
2446 HooksDispatcherOnUpdateInDEV = {
2459 - readContext<T>(
2460 - context: ReactContext<T>,
2461 - observedBits: void | number | boolean,
2462 - ): T {
2463 - return readContext(context, observedBits);
2447 + readContext<T>(context: ReactContext<T>): T {
2448 + return readContext(context);
2449 },
2450 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2451 currentHookNameInDev = 'useCallback';
2452 updateHookTypesDev();
2453 return updateCallback(callback, deps);
2454 },
2470 - useContext<T>(
2471 - context: ReactContext<T>,
2472 - observedBits: void | number | boolean,
2473 - ): T {
2455 + useContext<T>(context: ReactContext<T>): T {
2456 currentHookNameInDev = 'useContext';
2457 updateHookTypesDev();
2476 - return readContext(context, observedBits);
2458 + return readContext(context);
2459 },
2460 useEffect(
2461 create: () => (() => void) | void,
@@ -2586,11 +2568,8 @@ if (__DEV__) {
2568 }
2569
2570 HooksDispatcherOnRerenderInDEV = {
2589 - readContext<T>(
2590 - context: ReactContext<T>,
2591 - observedBits: void | number | boolean,
2592 - ): T {
2593 - return readContext(context, observedBits);
2571 + readContext<T>(context: ReactContext<T>): T {
2572 + return readContext(context);
2573 },
2574
2575 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
@@ -2598,13 +2577,10 @@ if (__DEV__) {
2577 updateHookTypesDev();
2578 return updateCallback(callback, deps);
2579 },
2601 - useContext<T>(
2602 - context: ReactContext<T>,
2603 - observedBits: void | number | boolean,
2604 - ): T {
2580 + useContext<T>(context: ReactContext<T>): T {
2581 currentHookNameInDev = 'useContext';
2582 updateHookTypesDev();
2607 - return readContext(context, observedBits);
2583 + return readContext(context);
2584 },
2585 useEffect(
2586 create: () => (() => void) | void,
@@ -2717,12 +2693,9 @@ if (__DEV__) {
2693 }
2694
2695 InvalidNestedHooksDispatcherOnMountInDEV = {
2720 - readContext<T>(
2721 - context: ReactContext<T>,
2722 - observedBits: void | number | boolean,
2723 - ): T {
2696 + readContext<T>(context: ReactContext<T>): T {
2697 warnInvalidContextAccess();
2725 - return readContext(context, observedBits);
2698 + return readContext(context);
2699 },
2700 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2701 currentHookNameInDev = 'useCallback';
@@ -2730,14 +2703,11 @@ if (__DEV__) {
2703 mountHookTypesDev();
2704 return mountCallback(callback, deps);
2705 },
2733 - useContext<T>(
2734 - context: ReactContext<T>,
2735 - observedBits: void | number | boolean,
2736 - ): T {
2706 + useContext<T>(context: ReactContext<T>): T {
2707 currentHookNameInDev = 'useContext';
2708 warnInvalidHookAccess();
2709 mountHookTypesDev();
2740 - return readContext(context, observedBits);
2710 + return readContext(context);
2711 },
2712 useEffect(
2713 create: () => (() => void) | void,
@@ -2862,12 +2832,9 @@ if (__DEV__) {
2832 }
2833
2834 InvalidNestedHooksDispatcherOnUpdateInDEV = {
2865 - readContext<T>(
2866 - context: ReactContext<T>,
2867 - observedBits: void | number | boolean,
2868 - ): T {
2835 + readContext<T>(context: ReactContext<T>): T {
2836 warnInvalidContextAccess();
2870 - return readContext(context, observedBits);
2837 + return readContext(context);
2838 },
2839 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2840 currentHookNameInDev = 'useCallback';
@@ -2875,14 +2842,11 @@ if (__DEV__) {
2842 updateHookTypesDev();
2843 return updateCallback(callback, deps);
2844 },
2878 - useContext<T>(
2879 - context: ReactContext<T>,
2880 - observedBits: void | number | boolean,
2881 - ): T {
2845 + useContext<T>(context: ReactContext<T>): T {
2846 currentHookNameInDev = 'useContext';
2847 warnInvalidHookAccess();
2848 updateHookTypesDev();
2885 - return readContext(context, observedBits);
2849 + return readContext(context);
2850 },
2851 useEffect(
2852 create: () => (() => void) | void,
@@ -3007,12 +2971,9 @@ if (__DEV__) {
2971 }
2972
2973 InvalidNestedHooksDispatcherOnRerenderInDEV = {
3010 - readContext<T>(
3011 - context: ReactContext<T>,
3012 - observedBits: void | number | boolean,
3013 - ): T {
2974 + readContext<T>(context: ReactContext<T>): T {
2975 warnInvalidContextAccess();
3015 - return readContext(context, observedBits);
2976 + return readContext(context);
2977 },
2978
2979 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
@@ -3021,14 +2982,11 @@ if (__DEV__) {
2982 updateHookTypesDev();
2983 return updateCallback(callback, deps);
2984 },
3024 - useContext<T>(
3025 - context: ReactContext<T>,
3026 - observedBits: void | number | boolean,
3027 - ): T {
2985 + useContext<T>(context: ReactContext<T>): T {
2986 currentHookNameInDev = 'useContext';
2987 warnInvalidHookAccess();
2988 updateHookTypesDev();
3031 - return readContext(context, observedBits);
2989 + return readContext(context);
2990 },
2991 useEffect(
2992 create: () => (() => void) | void,
packages/react-reconciler/src/ReactFiberHooks.old.js
+28 -70
@@ -2191,11 +2191,8 @@ if (__DEV__) {
2191 };
2192
2193 HooksDispatcherOnMountInDEV = {
2194 - readContext<T>(
2195 - context: ReactContext<T>,
2196 - observedBits: void | number | boolean,
2197 - ): T {
2198 - return readContext(context, observedBits);
2194 + readContext<T>(context: ReactContext<T>): T {
2195 + return readContext(context);
2196 },
2197 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2198 currentHookNameInDev = 'useCallback';
@@ -2203,13 +2200,10 @@ if (__DEV__) {
2200 checkDepsAreArrayDev(deps);
2201 return mountCallback(callback, deps);
2202 },
2206 - useContext<T>(
2207 - context: ReactContext<T>,
2208 - observedBits: void | number | boolean,
2209 - ): T {
2203 + useContext<T>(context: ReactContext<T>): T {
2204 currentHookNameInDev = 'useContext';
2205 mountHookTypesDev();
2212 - return readContext(context, observedBits);
2206 + return readContext(context);
2207 },
2208 useEffect(
2209 create: () => (() => void) | void,
@@ -2326,24 +2320,18 @@ if (__DEV__) {
2320 }
2321
2322 HooksDispatcherOnMountWithHookTypesInDEV = {
2329 - readContext<T>(
2330 - context: ReactContext<T>,
2331 - observedBits: void | number | boolean,
2332 - ): T {
2333 - return readContext(context, observedBits);
2323 + readContext<T>(context: ReactContext<T>): T {
2324 + return readContext(context);
2325 },
2326 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2327 currentHookNameInDev = 'useCallback';
2328 updateHookTypesDev();
2329 return mountCallback(callback, deps);
2330 },
2340 - useContext<T>(
2341 - context: ReactContext<T>,
2342 - observedBits: void | number | boolean,
2343 - ): T {
2331 + useContext<T>(context: ReactContext<T>): T {
2332 currentHookNameInDev = 'useContext';
2333 updateHookTypesDev();
2346 - return readContext(context, observedBits);
2334 + return readContext(context);
2335 },
2336 useEffect(
2337 create: () => (() => void) | void,
@@ -2456,24 +2444,18 @@ if (__DEV__) {
2444 }
2445
2446 HooksDispatcherOnUpdateInDEV = {
2459 - readContext<T>(
2460 - context: ReactContext<T>,
2461 - observedBits: void | number | boolean,
2462 - ): T {
2463 - return readContext(context, observedBits);
2447 + readContext<T>(context: ReactContext<T>): T {
2448 + return readContext(context);
2449 },
2450 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2451 currentHookNameInDev = 'useCallback';
2452 updateHookTypesDev();
2453 return updateCallback(callback, deps);
2454 },
2470 - useContext<T>(
2471 - context: ReactContext<T>,
2472 - observedBits: void | number | boolean,
2473 - ): T {
2455 + useContext<T>(context: ReactContext<T>): T {
2456 currentHookNameInDev = 'useContext';
2457 updateHookTypesDev();
2476 - return readContext(context, observedBits);
2458 + return readContext(context);
2459 },
2460 useEffect(
2461 create: () => (() => void) | void,
@@ -2586,11 +2568,8 @@ if (__DEV__) {
2568 }
2569
2570 HooksDispatcherOnRerenderInDEV = {
2589 - readContext<T>(
2590 - context: ReactContext<T>,
2591 - observedBits: void | number | boolean,
2592 - ): T {
2593 - return readContext(context, observedBits);
2571 + readContext<T>(context: ReactContext<T>): T {
2572 + return readContext(context);
2573 },
2574
2575 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
@@ -2598,13 +2577,10 @@ if (__DEV__) {
2577 updateHookTypesDev();
2578 return updateCallback(callback, deps);
2579 },
2601 - useContext<T>(
2602 - context: ReactContext<T>,
2603 - observedBits: void | number | boolean,
2604 - ): T {
2580 + useContext<T>(context: ReactContext<T>): T {
2581 currentHookNameInDev = 'useContext';
2582 updateHookTypesDev();
2607 - return readContext(context, observedBits);
2583 + return readContext(context);
2584 },
2585 useEffect(
2586 create: () => (() => void) | void,
@@ -2717,12 +2693,9 @@ if (__DEV__) {
2693 }
2694
2695 InvalidNestedHooksDispatcherOnMountInDEV = {
2720 - readContext<T>(
2721 - context: ReactContext<T>,
2722 - observedBits: void | number | boolean,
2723 - ): T {
2696 + readContext<T>(context: ReactContext<T>): T {
2697 warnInvalidContextAccess();
2725 - return readContext(context, observedBits);
2698 + return readContext(context);
2699 },
2700 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2701 currentHookNameInDev = 'useCallback';
@@ -2730,14 +2703,11 @@ if (__DEV__) {
2703 mountHookTypesDev();
2704 return mountCallback(callback, deps);
2705 },
2733 - useContext<T>(
2734 - context: ReactContext<T>,
2735 - observedBits: void | number | boolean,
2736 - ): T {
2706 + useContext<T>(context: ReactContext<T>): T {
2707 currentHookNameInDev = 'useContext';
2708 warnInvalidHookAccess();
2709 mountHookTypesDev();
2740 - return readContext(context, observedBits);
2710 + return readContext(context);
2711 },
2712 useEffect(
2713 create: () => (() => void) | void,
@@ -2862,12 +2832,9 @@ if (__DEV__) {
2832 }
2833
2834 InvalidNestedHooksDispatcherOnUpdateInDEV = {
2865 - readContext<T>(
2866 - context: ReactContext<T>,
2867 - observedBits: void | number | boolean,
2868 - ): T {
2835 + readContext<T>(context: ReactContext<T>): T {
2836 warnInvalidContextAccess();
2870 - return readContext(context, observedBits);
2837 + return readContext(context);
2838 },
2839 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2840 currentHookNameInDev = 'useCallback';
@@ -2875,14 +2842,11 @@ if (__DEV__) {
2842 updateHookTypesDev();
2843 return updateCallback(callback, deps);
2844 },
2878 - useContext<T>(
2879 - context: ReactContext<T>,
2880 - observedBits: void | number | boolean,
2881 - ): T {
2845 + useContext<T>(context: ReactContext<T>): T {
2846 currentHookNameInDev = 'useContext';
2847 warnInvalidHookAccess();
2848 updateHookTypesDev();
2885 - return readContext(context, observedBits);
2849 + return readContext(context);
2850 },
2851 useEffect(
2852 create: () => (() => void) | void,
@@ -3007,12 +2971,9 @@ if (__DEV__) {
2971 }
2972
2973 InvalidNestedHooksDispatcherOnRerenderInDEV = {
3010 - readContext<T>(
3011 - context: ReactContext<T>,
3012 - observedBits: void | number | boolean,
3013 - ): T {
2974 + readContext<T>(context: ReactContext<T>): T {
2975 warnInvalidContextAccess();
3015 - return readContext(context, observedBits);
2976 + return readContext(context);
2977 },
2978
2979 useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
@@ -3021,14 +2982,11 @@ if (__DEV__) {
2982 updateHookTypesDev();
2983 return updateCallback(callback, deps);
2984 },
3024 - useContext<T>(
3025 - context: ReactContext<T>,
3026 - observedBits: void | number | boolean,
3027 - ): T {
2985 + useContext<T>(context: ReactContext<T>): T {
2986 currentHookNameInDev = 'useContext';
2987 warnInvalidHookAccess();
2988 updateHookTypesDev();
3031 - return readContext(context, observedBits);
2989 + return readContext(context);
2990 },
2991 useEffect(
2992 create: () => (() => void) | void,
packages/react-reconciler/src/ReactFiberNewContext.new.js
+13 -74
@@ -19,7 +19,6 @@ import type {SharedQueue} from './ReactUpdateQueue.new';
19
20 import {isPrimaryRenderer} from './ReactFiberHostConfig';
21 import {createCursor, push, pop} from './ReactFiberStack.new';
22 -import {MAX_SIGNED_31_BIT_INT} from './MaxInts';
22 import {
23 ContextProvider,
24 ClassComponent,
@@ -58,7 +57,7 @@ if (__DEV__) {
57
58 let currentlyRenderingFiber: Fiber | null = null;
59 let lastContextDependency: ContextDependency<mixed> | null = null;
61 -let lastContextWithAllBitsObserved: ReactContext<any> | null = null;
60 +let lastFullyObservedContext: ReactContext<any> | null = null;
61
62 let isDisallowedContextReadInDEV: boolean = false;
63
@@ -67,7 +66,7 @@ export function resetContextDependencies(): void {
66 // cannot be called outside the render phase.
67 currentlyRenderingFiber = null;
68 lastContextDependency = null;
70 - lastContextWithAllBitsObserved = null;
69 + lastFullyObservedContext = null;
70 if (__DEV__) {
71 isDisallowedContextReadInDEV = false;
72 }
@@ -140,33 +139,6 @@ export function popProvider(
139 }
140 }
141
143 -export function calculateChangedBits<T>(
144 - context: ReactContext<T>,
145 - newValue: T,
146 - oldValue: T,
147 -) {
148 - if (is(oldValue, newValue)) {
149 - // No change
150 - return 0;
151 - } else {
152 - const changedBits =
153 - typeof context._calculateChangedBits === 'function'
154 - ? context._calculateChangedBits(oldValue, newValue)
155 - : MAX_SIGNED_31_BIT_INT;
156 -
157 - if (__DEV__) {
158 - if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) {
159 - console.error(
160 - 'calculateChangedBits: Expected the return value to be a ' +
161 - '31-bit integer. Instead received: %s',
162 - changedBits,
163 - );
164 - }
165 - }
166 - return changedBits | 0;
167 - }
168 -}
169 -
142 export function scheduleWorkOnParentPath(
143 parent: Fiber | null,
144 renderLanes: Lanes,
@@ -197,7 +169,6 @@ export function scheduleWorkOnParentPath(
169 export function propagateContextChange<T>(
170 workInProgress: Fiber,
171 context: ReactContext<T>,
200 - changedBits: number,
172 renderLanes: Lanes,
173 ): void {
174 if (enableLazyContextPropagation) {
@@ -207,24 +178,18 @@ export function propagateContextChange<T>(
178 const forcePropagateEntireTree = true;
179 propagateContextChanges(
180 workInProgress,
210 - [context, changedBits],
181 + [context],
182 renderLanes,
183 forcePropagateEntireTree,
184 );
185 } else {
215 - propagateContextChange_eager(
216 - workInProgress,
217 - context,
218 - changedBits,
219 - renderLanes,
220 - );
186 + propagateContextChange_eager(workInProgress, context, renderLanes);
187 }
188 }
189
190 function propagateContextChange_eager<T>(
191 workInProgress: Fiber,
192 context: ReactContext<T>,
227 - changedBits: number,
193 renderLanes: Lanes,
194 ): void {
195 // Only used by eager implemenation
@@ -247,10 +212,7 @@ function propagateContextChange_eager<T>(
212 let dependency = list.firstContext;
213 while (dependency !== null) {
214 // Check if the context matches.
250 - if (
251 - dependency.context === context &&
252 - (dependency.observedBits & changedBits) !== 0
253 - ) {
215 + if (dependency.context === context) {
216 // Match! Schedule an update on this fiber.
217 if (fiber.tag === ClassComponent) {
218 // Schedule a force update on the work-in-progress.
@@ -382,15 +344,11 @@ function propagateContextChanges<T>(
344 // Assigning these to constants to help Flow
345 const dependency = dep;
346 const consumer = fiber;
385 - findContext: for (let i = 0; i < contexts.length; i += 2) {
347 + findContext: for (let i = 0; i < contexts.length; i++) {
348 const context: ReactContext<T> = contexts[i];
387 - const changedBits: number = contexts[i + 1];
349 // Check if the context matches.
350 // TODO: Compare selected values to bail out early.
390 - if (
391 - dependency.context === context &&
392 - (dependency.observedBits & changedBits) !== 0
393 - ) {
351 + if (dependency.context === context) {
352 // Match! Schedule an update on this fiber.
353
354 // In the lazy implemenation, don't mark a dirty flag on the
@@ -549,12 +507,11 @@ function propagateParentContextChanges(
507
508 const oldValue = oldProps.value;
509
552 - const changedBits = calculateChangedBits(context, newValue, oldValue);
553 - if (changedBits !== 0) {
510 + if (!is(newValue, oldValue)) {
511 if (contexts !== null) {
555 - contexts.push(context, changedBits);
512 + contexts.push(context);
513 } else {
557 - contexts = [context, changedBits];
514 + contexts = [context];
515 }
516 }
517 }
@@ -625,7 +582,7 @@ export function prepareToReadContext(
582 ): void {
583 currentlyRenderingFiber = workInProgress;
584 lastContextDependency = null;
628 - lastContextWithAllBitsObserved = null;
585 + lastFullyObservedContext = null;
586
587 const dependencies = workInProgress.dependencies;
588 if (dependencies !== null) {
@@ -646,10 +603,7 @@ export function prepareToReadContext(
603 }
604 }
605
649 -export function readContext<T>(
650 - context: ReactContext<T>,
651 - observedBits: void | number | boolean,
652 -): T {
606 +export function readContext<T>(context: ReactContext<T>): T {
607 if (__DEV__) {
608 // This warning would fire if you read context inside a Hook like useMemo.
609 // Unlike the class check below, it's not enforced in production for perf.
@@ -667,26 +621,11 @@ export function readContext<T>(
621 ? context._currentValue
622 : context._currentValue2;
623
670 - if (lastContextWithAllBitsObserved === context) {
624 + if (lastFullyObservedContext === context) {
625 // Nothing to do. We already observe everything in this context.
672 - } else if (observedBits === false || observedBits === 0) {
673 - // Do not observe any updates.
626 } else {
675 - let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types.
676 - if (
677 - typeof observedBits !== 'number' ||
678 - observedBits === MAX_SIGNED_31_BIT_INT
679 - ) {
680 - // Observe all updates.
681 - lastContextWithAllBitsObserved = ((context: any): ReactContext<mixed>);
682 - resolvedObservedBits = MAX_SIGNED_31_BIT_INT;
683 - } else {
684 - resolvedObservedBits = observedBits;
685 - }
686 -
627 const contextItem = {
628 context: ((context: any): ReactContext<mixed>),
689 - observedBits: resolvedObservedBits,
629 memoizedValue: value,
630 next: null,
631 };
packages/react-reconciler/src/ReactFiberNewContext.old.js
+13 -74
@@ -19,7 +19,6 @@ import type {SharedQueue} from './ReactUpdateQueue.old';
19
20 import {isPrimaryRenderer} from './ReactFiberHostConfig';
21 import {createCursor, push, pop} from './ReactFiberStack.old';
22 -import {MAX_SIGNED_31_BIT_INT} from './MaxInts';
22 import {
23 ContextProvider,
24 ClassComponent,
@@ -58,7 +57,7 @@ if (__DEV__) {
57
58 let currentlyRenderingFiber: Fiber | null = null;
59 let lastContextDependency: ContextDependency<mixed> | null = null;
61 -let lastContextWithAllBitsObserved: ReactContext<any> | null = null;
60 +let lastFullyObservedContext: ReactContext<any> | null = null;
61
62 let isDisallowedContextReadInDEV: boolean = false;
63
@@ -67,7 +66,7 @@ export function resetContextDependencies(): void {
66 // cannot be called outside the render phase.
67 currentlyRenderingFiber = null;
68 lastContextDependency = null;
70 - lastContextWithAllBitsObserved = null;
69 + lastFullyObservedContext = null;
70 if (__DEV__) {
71 isDisallowedContextReadInDEV = false;
72 }
@@ -140,33 +139,6 @@ export function popProvider(
139 }
140 }
141
143 -export function calculateChangedBits<T>(
144 - context: ReactContext<T>,
145 - newValue: T,
146 - oldValue: T,
147 -) {
148 - if (is(oldValue, newValue)) {
149 - // No change
150 - return 0;
151 - } else {
152 - const changedBits =
153 - typeof context._calculateChangedBits === 'function'
154 - ? context._calculateChangedBits(oldValue, newValue)
155 - : MAX_SIGNED_31_BIT_INT;
156 -
157 - if (__DEV__) {
158 - if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) {
159 - console.error(
160 - 'calculateChangedBits: Expected the return value to be a ' +
161 - '31-bit integer. Instead received: %s',
162 - changedBits,
163 - );
164 - }
165 - }
166 - return changedBits | 0;
167 - }
168 -}
169 -
142 export function scheduleWorkOnParentPath(
143 parent: Fiber | null,
144 renderLanes: Lanes,
@@ -197,7 +169,6 @@ export function scheduleWorkOnParentPath(
169 export function propagateContextChange<T>(
170 workInProgress: Fiber,
171 context: ReactContext<T>,
200 - changedBits: number,
172 renderLanes: Lanes,
173 ): void {
174 if (enableLazyContextPropagation) {
@@ -207,24 +178,18 @@ export function propagateContextChange<T>(
178 const forcePropagateEntireTree = true;
179 propagateContextChanges(
180 workInProgress,
210 - [context, changedBits],
181 + [context],
182 renderLanes,
183 forcePropagateEntireTree,
184 );
185 } else {
215 - propagateContextChange_eager(
216 - workInProgress,
217 - context,
218 - changedBits,
219 - renderLanes,
220 - );
186 + propagateContextChange_eager(workInProgress, context, renderLanes);
187 }
188 }
189
190 function propagateContextChange_eager<T>(
191 workInProgress: Fiber,
192 context: ReactContext<T>,
227 - changedBits: number,
193 renderLanes: Lanes,
194 ): void {
195 // Only used by eager implemenation
@@ -247,10 +212,7 @@ function propagateContextChange_eager<T>(
212 let dependency = list.firstContext;
213 while (dependency !== null) {
214 // Check if the context matches.
250 - if (
251 - dependency.context === context &&
252 - (dependency.observedBits & changedBits) !== 0
253 - ) {
215 + if (dependency.context === context) {
216 // Match! Schedule an update on this fiber.
217 if (fiber.tag === ClassComponent) {
218 // Schedule a force update on the work-in-progress.
@@ -382,15 +344,11 @@ function propagateContextChanges<T>(
344 // Assigning these to constants to help Flow
345 const dependency = dep;
346 const consumer = fiber;
385 - findContext: for (let i = 0; i < contexts.length; i += 2) {
347 + findContext: for (let i = 0; i < contexts.length; i++) {
348 const context: ReactContext<T> = contexts[i];
387 - const changedBits: number = contexts[i + 1];
349 // Check if the context matches.
350 // TODO: Compare selected values to bail out early.
390 - if (
391 - dependency.context === context &&
392 - (dependency.observedBits & changedBits) !== 0
393 - ) {
351 + if (dependency.context === context) {
352 // Match! Schedule an update on this fiber.
353
354 // In the lazy implemenation, don't mark a dirty flag on the
@@ -549,12 +507,11 @@ function propagateParentContextChanges(
507
508 const oldValue = oldProps.value;
509
552 - const changedBits = calculateChangedBits(context, newValue, oldValue);
553 - if (changedBits !== 0) {
510 + if (!is(newValue, oldValue)) {
511 if (contexts !== null) {
555 - contexts.push(context, changedBits);
512 + contexts.push(context);
513 } else {
557 - contexts = [context, changedBits];
514 + contexts = [context];
515 }
516 }
517 }
@@ -625,7 +582,7 @@ export function prepareToReadContext(
582 ): void {
583 currentlyRenderingFiber = workInProgress;
584 lastContextDependency = null;
628 - lastContextWithAllBitsObserved = null;
585 + lastFullyObservedContext = null;
586
587 const dependencies = workInProgress.dependencies;
588 if (dependencies !== null) {
@@ -646,10 +603,7 @@ export function prepareToReadContext(
603 }
604 }
605
649 -export function readContext<T>(
650 - context: ReactContext<T>,
651 - observedBits: void | number | boolean,
652 -): T {
606 +export function readContext<T>(context: ReactContext<T>): T {
607 if (__DEV__) {
608 // This warning would fire if you read context inside a Hook like useMemo.
609 // Unlike the class check below, it's not enforced in production for perf.
@@ -667,26 +621,11 @@ export function readContext<T>(
621 ? context._currentValue
622 : context._currentValue2;
623
670 - if (lastContextWithAllBitsObserved === context) {
624 + if (lastFullyObservedContext === context) {
625 // Nothing to do. We already observe everything in this context.
672 - } else if (observedBits === false || observedBits === 0) {
673 - // Do not observe any updates.
626 } else {
675 - let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types.
676 - if (
677 - typeof observedBits !== 'number' ||
678 - observedBits === MAX_SIGNED_31_BIT_INT
679 - ) {
680 - // Observe all updates.
681 - lastContextWithAllBitsObserved = ((context: any): ReactContext<mixed>);
682 - resolvedObservedBits = MAX_SIGNED_31_BIT_INT;
683 - } else {
684 - resolvedObservedBits = observedBits;
685 - }
686 -
627 const contextItem = {
628 context: ((context: any): ReactContext<mixed>),
689 - observedBits: resolvedObservedBits,
629 memoizedValue: value,
630 next: null,
631 };
packages/react-reconciler/src/ReactInternalTypes.js
+2 -9
@@ -49,7 +49,6 @@ export type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90;
49
50 export type ContextDependency<T> = {
51 context: ReactContext<T>,
52 - observedBits: number,
52 next: ContextDependency<mixed> | null,
53 memoizedValue: T,
54 ...
@@ -281,20 +280,14 @@ type Dispatch<A> = A => void;
280
281 export type Dispatcher = {|
282 getCacheForType?: <T>(resourceType: () => T) => T,
284 - readContext<T>(
285 - context: ReactContext<T>,
286 - observedBits: void | number | boolean,
287 - ): T,
283 + readContext<T>(context: ReactContext<T>): T,
284 useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>],
285 useReducer<S, I, A>(
286 reducer: (S, A) => S,
287 initialArg: I,
288 init?: (I) => S,
289 ): [S, Dispatch<A>],
294 - useContext<T>(
295 - context: ReactContext<T>,
296 - observedBits: void | number | boolean,
297 - ): T,
290 + useContext<T>(context: ReactContext<T>): T,
291 useRef<T>(initialValue: T): {|current: T|},
292 useEffect(
293 create: () => (() => void) | void,
packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+10 -297
@@ -35,11 +35,11 @@ describe('ReactNewContext', () => {
35 return {type: 'span', children: [], prop, hidden: false};
36 }
37
38 - function readContext(Context, observedBits) {
38 + function readContext(Context) {
39 const dispatcher =
40 React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
41 .ReactCurrentDispatcher.current;
42 - return dispatcher.readContext(Context, observedBits);
42 + return dispatcher.readContext(Context);
43 }
44
45 // Note: This is based on a similar component we use in www. We can delete
@@ -62,48 +62,21 @@ describe('ReactNewContext', () => {
62 'useContext inside function component',
63 Context =>
64 function Consumer(props) {
65 - const observedBits = props.unstable_observedBits;
66 - let contextValue;
67 - expect(() => {
68 - contextValue = useContext(Context, observedBits);
69 - }).toErrorDev(
70 - observedBits !== undefined
71 - ? 'useContext() second argument is reserved for future use in React. ' +
72 - `Passing it is not supported. You passed: ${observedBits}.`
73 - : [],
74 - );
65 + const contextValue = useContext(Context);
66 const render = props.children;
67 return render(contextValue);
68 },
69 );
70 sharedContextTests('useContext inside forwardRef component', Context =>
71 React.forwardRef(function Consumer(props, ref) {
81 - const observedBits = props.unstable_observedBits;
82 - let contextValue;
83 - expect(() => {
84 - contextValue = useContext(Context, observedBits);
85 - }).toErrorDev(
86 - observedBits !== undefined
87 - ? 'useContext() second argument is reserved for future use in React. ' +
88 - `Passing it is not supported. You passed: ${observedBits}.`
89 - : [],
90 - );
72 + const contextValue = useContext(Context);
73 const render = props.children;
74 return render(contextValue);
75 }),
76 );
77 sharedContextTests('useContext inside memoized function component', Context =>
78 React.memo(function Consumer(props) {
97 - const observedBits = props.unstable_observedBits;
98 - let contextValue;
99 - expect(() => {
100 - contextValue = useContext(Context, observedBits);
101 - }).toErrorDev(
102 - observedBits !== undefined
103 - ? 'useContext() second argument is reserved for future use in React. ' +
104 - `Passing it is not supported. You passed: ${observedBits}.`
105 - : [],
106 - );
79 + const contextValue = useContext(Context);
80 const render = props.children;
81 return render(contextValue);
82 }),
@@ -113,8 +86,7 @@ describe('ReactNewContext', () => {
86 Context =>
87 class Consumer extends React.Component {
88 render() {
116 - const observedBits = this.props.unstable_observedBits;
117 - const contextValue = readContext(Context, observedBits);
89 + const contextValue = readContext(Context);
90 const render = this.props.children;
91 return render(contextValue);
92 }
@@ -125,8 +97,7 @@ describe('ReactNewContext', () => {
97 Context =>
98 class Consumer extends React.PureComponent {
99 render() {
128 - const observedBits = this.props.unstable_observedBits;
129 - const contextValue = readContext(Context, observedBits);
100 + const contextValue = readContext(Context);
101 const render = this.props.children;
102 return render(contextValue);
103 }
@@ -590,227 +561,6 @@ describe('ReactNewContext', () => {
561 ]);
562 });
563
593 - it('can skip consumers with bitmask', () => {
594 - const Context = React.createContext({foo: 0, bar: 0}, (a, b) => {
595 - let result = 0;
596 - if (a.foo !== b.foo) {
597 - result |= 0b01;
598 - }
599 - if (a.bar !== b.bar) {
600 - result |= 0b10;
601 - }
602 - return result;
603 - });
604 - const Consumer = getConsumer(Context);
605 -
606 - function Provider(props) {
607 - return (
608 - <Context.Provider value={{foo: props.foo, bar: props.bar}}>
609 - {props.children}
610 - </Context.Provider>
611 - );
612 - }
613 -
614 - function Foo() {
615 - return (
616 - <Consumer unstable_observedBits={0b01}>
617 - {value => {
618 - Scheduler.unstable_yieldValue('Foo');
619 - return <span prop={'Foo: ' + value.foo} />;
620 - }}
621 - </Consumer>
622 - );
623 - }
624 -
625 - function Bar() {
626 - return (
627 - <Consumer unstable_observedBits={0b10}>
628 - {value => {
629 - Scheduler.unstable_yieldValue('Bar');
630 - return <span prop={'Bar: ' + value.bar} />;
631 - }}
632 - </Consumer>
633 - );
634 - }
635 -
636 - class Indirection extends React.Component {
637 - shouldComponentUpdate() {
638 - return false;
639 - }
640 - render() {
641 - return this.props.children;
642 - }
643 - }
644 -
645 - function App(props) {
646 - return (
647 - <Provider foo={props.foo} bar={props.bar}>
648 - <Indirection>
649 - <Indirection>
650 - <Foo />
651 - </Indirection>
652 - <Indirection>
653 - <Bar />
654 - </Indirection>
655 - </Indirection>
656 - </Provider>
657 - );
658 - }
659 -
660 - ReactNoop.render(<App foo={1} bar={1} />);
661 - expect(Scheduler).toFlushAndYield(['Foo', 'Bar']);
662 - expect(ReactNoop.getChildren()).toEqual([
663 - span('Foo: 1'),
664 - span('Bar: 1'),
665 - ]);
666 -
667 - // Update only foo
668 - ReactNoop.render(<App foo={2} bar={1} />);
669 - expect(Scheduler).toFlushAndYield(['Foo']);
670 - expect(ReactNoop.getChildren()).toEqual([
671 - span('Foo: 2'),
672 - span('Bar: 1'),
673 - ]);
674 -
675 - // Update only bar
676 - ReactNoop.render(<App foo={2} bar={2} />);
677 - expect(Scheduler).toFlushAndYield(['Bar']);
678 - expect(ReactNoop.getChildren()).toEqual([
679 - span('Foo: 2'),
680 - span('Bar: 2'),
681 - ]);
682 -
683 - // Update both
684 - ReactNoop.render(<App foo={3} bar={3} />);
685 - expect(Scheduler).toFlushAndYield(['Foo', 'Bar']);
686 - expect(ReactNoop.getChildren()).toEqual([
687 - span('Foo: 3'),
688 - span('Bar: 3'),
689 - ]);
690 - });
691 -
692 - // @gate !enableLazyContextPropagation
693 - it('can skip parents with bitmask bailout while updating their children', () => {
694 - const Context = React.createContext({foo: 0, bar: 0}, (a, b) => {
695 - let result = 0;
696 - if (a.foo !== b.foo) {
697 - result |= 0b01;
698 - }
699 - if (a.bar !== b.bar) {
700 - result |= 0b10;
701 - }
702 - return result;
703 - });
704 - const Consumer = getConsumer(Context);
705 -
706 - function Provider(props) {
707 - return (
708 - <Context.Provider value={{foo: props.foo, bar: props.bar}}>
709 - {props.children}
710 - </Context.Provider>
711 - );
712 - }
713 -
714 - function Foo(props) {
715 - return (
716 - <Consumer unstable_observedBits={0b01}>
717 - {value => {
718 - Scheduler.unstable_yieldValue('Foo');
719 - return (
720 - <>
721 - <span prop={'Foo: ' + value.foo} />
722 - {props.children && props.children()}
723 - </>
724 - );
725 - }}
726 - </Consumer>
727 - );
728 - }
729 -
730 - function Bar(props) {
731 - return (
732 - <Consumer unstable_observedBits={0b10}>
733 - {value => {
734 - Scheduler.unstable_yieldValue('Bar');
735 - return (
736 - <>
737 - <span prop={'Bar: ' + value.bar} />
738 - {props.children && props.children()}
739 - </>
740 - );
741 - }}
742 - </Consumer>
743 - );
744 - }
745 -
746 - class Indirection extends React.Component {
747 - shouldComponentUpdate() {
748 - return false;
749 - }
750 - render() {
751 - return this.props.children;
752 - }
753 - }
754 -
755 - function App(props) {
756 - return (
757 - <Provider foo={props.foo} bar={props.bar}>
758 - <Indirection>
759 - <Foo>
760 - {/* Use a render prop so we don't test constant elements. */}
761 - {() => (
762 - <Indirection>
763 - <Bar>
764 - {() => (
765 - <Indirection>
766 - <Foo />
767 - </Indirection>
768 - )}
769 - </Bar>
770 - </Indirection>
771 - )}
772 - </Foo>
773 - </Indirection>
774 - </Provider>
775 - );
776 - }
777 -
778 - ReactNoop.render(<App foo={1} bar={1} />);
779 - expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Foo']);
780 - expect(ReactNoop.getChildren()).toEqual([
781 - span('Foo: 1'),
782 - span('Bar: 1'),
783 - span('Foo: 1'),
784 - ]);
785 -
786 - // Update only foo
787 - ReactNoop.render(<App foo={2} bar={1} />);
788 - expect(Scheduler).toFlushAndYield(['Foo', 'Foo']);
789 - expect(ReactNoop.getChildren()).toEqual([
790 - span('Foo: 2'),
791 - span('Bar: 1'),
792 - span('Foo: 2'),
793 - ]);
794 -
795 - // Update only bar
796 - ReactNoop.render(<App foo={2} bar={2} />);
797 - expect(Scheduler).toFlushAndYield(['Bar']);
798 - expect(ReactNoop.getChildren()).toEqual([
799 - span('Foo: 2'),
800 - span('Bar: 2'),
801 - span('Foo: 2'),
802 - ]);
803 -
804 - // Update both
805 - ReactNoop.render(<App foo={3} bar={3} />);
806 - expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Foo']);
807 - expect(ReactNoop.getChildren()).toEqual([
808 - span('Foo: 3'),
809 - span('Bar: 3'),
810 - span('Foo: 3'),
811 - ]);
812 - });
813 -
564 it("does not re-render if there's an update in a child", () => {
565 const Context = React.createContext(0);
566 const Consumer = getConsumer(Context);
@@ -1063,30 +813,6 @@ describe('ReactNewContext', () => {
813 }
814
815 describe('Context.Provider', () => {
1066 - it('warns if calculateChangedBits returns larger than a 31-bit integer', () => {
1067 - const Context = React.createContext(
1068 - 0,
1069 - (a, b) => Math.pow(2, 32) - 1, // Return 32 bit int
1070 - );
1071 -
1072 - function App(props) {
1073 - return <Context.Provider value={props.value} />;
1074 - }
1075 -
1076 - ReactNoop.render(<App value={1} />);
1077 - expect(Scheduler).toFlushWithoutYielding();
1078 -
1079 - // Update
1080 - ReactNoop.render(<App value={2} />);
1081 -
1082 - if (gate(flags => !flags.enableLazyContextPropagation)) {
1083 - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
1084 - 'calculateChangedBits: Expected the return value to be a 31-bit ' +
1085 - 'integer. Instead received: 4294967295',
1086 - );
1087 - }
1088 - });
1089 -
816 it('warns if no value prop provided', () => {
817 const Context = React.createContext();
818
@@ -1355,6 +1081,9 @@ describe('ReactNewContext', () => {
1081 });
1082
1083 describe('readContext', () => {
1084 + // Unstable changedBits API was removed. Port this test to context selectors
1085 + // once that exists.
1086 + // @gate FIXME
1087 it('can read the same context multiple times in the same function', () => {
1088 const Context = React.createContext({foo: 0, bar: 0, baz: 0}, (a, b) => {
1089 let result = 0;
@@ -1517,22 +1246,6 @@ describe('ReactNewContext', () => {
1246 });
1247
1248 describe('useContext', () => {
1520 - it('warns on array.map(useContext)', () => {
1521 - const Context = React.createContext(0);
1522 - function Foo() {
1523 - return [Context].map(useContext);
1524 - }
1525 - ReactNoop.render(<Foo />);
1526 - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
1527 - 'useContext() second argument is reserved for future ' +
1528 - 'use in React. Passing it is not supported. ' +
1529 - 'You passed: 0.\n\n' +
1530 - 'Did you call array.map(useContext)? ' +
1531 - 'Calling Hooks inside a loop is not supported. ' +
1532 - 'Learn more at https://reactjs.org/link/rules-of-hooks',
1533 - );
1534 - });
1535 -
1249 it('throws when used in a class component', () => {
1250 const Context = React.createContext(0);
1251 class Foo extends React.Component {
packages/react/src/ReactContext.js
+3 -22
@@ -11,30 +11,12 @@ import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
11
12 import type {ReactContext} from 'shared/ReactTypes';
13
14 -export function createContext<T>(
15 - defaultValue: T,
16 - calculateChangedBits: ?(a: T, b: T) => number,
17 -): ReactContext<T> {
18 - if (calculateChangedBits === undefined) {
19 - calculateChangedBits = null;
20 - } else {
21 - if (__DEV__) {
22 - if (
23 - calculateChangedBits !== null &&
24 - typeof calculateChangedBits !== 'function'
25 - ) {
26 - console.error(
27 - 'createContext: Expected the optional second argument to be a ' +
28 - 'function. Instead received: %s',
29 - calculateChangedBits,
30 - );
31 - }
32 - }
33 - }
14 +export function createContext<T>(defaultValue: T): ReactContext<T> {
15 + // TODO: Second argument used to be an optional `calculateChangedBits`
16 + // function. Warn to reserve for future use?
17
18 const context: ReactContext<T> = {
19 $$typeof: REACT_CONTEXT_TYPE,
37 - _calculateChangedBits: calculateChangedBits,
20 // As a workaround to support multiple concurrent renderers, we categorize
21 // some renderers as primary and others as secondary. We only expect
22 // there to be two concurrent renderers at most: React Native (primary) and
@@ -66,7 +48,6 @@ export function createContext<T>(
48 const Consumer = {
49 $$typeof: REACT_CONTEXT_TYPE,
50 _context: context,
69 - _calculateChangedBits: context._calculateChangedBits,
51 };
52 // $FlowFixMe: Flow complains about not setting a value, which is intentional here
53 Object.defineProperties(Consumer, {
packages/react/src/ReactHooks.js
+2 -19
@@ -47,26 +47,9 @@ export function getCacheForType<T>(resourceType: () => T): T {
47 return dispatcher.getCacheForType(resourceType);
48 }
49
50 -export function useContext<T>(
51 - Context: ReactContext<T>,
52 - unstable_observedBits: number | boolean | void,
53 -): T {
50 +export function useContext<T>(Context: ReactContext<T>): T {
51 const dispatcher = resolveDispatcher();
52 if (__DEV__) {
56 - if (unstable_observedBits !== undefined) {
57 - console.error(
58 - 'useContext() second argument is reserved for future ' +
59 - 'use in React. Passing it is not supported. ' +
60 - 'You passed: %s.%s',
61 - unstable_observedBits,
62 - typeof unstable_observedBits === 'number' && Array.isArray(arguments[2])
63 - ? '\n\nDid you call array.map(useContext)? ' +
64 - 'Calling Hooks inside a loop is not supported. ' +
65 - 'Learn more at https://reactjs.org/link/rules-of-hooks'
66 - : '',
67 - );
68 - }
69 -
53 // TODO: add a more generic warning for invalid values.
54 if ((Context: any)._context !== undefined) {
55 const realContext = (Context: any)._context;
@@ -85,7 +68,7 @@ export function useContext<T>(
68 }
69 }
70 }
88 - return dispatcher.useContext(Context, unstable_observedBits);
71 + return dispatcher.useContext(Context);
72 }
73
74 export function useState<S>(
packages/shared/ReactTypes.js
-2
@@ -49,7 +49,6 @@ export type ReactConsumer<T> = {
49 ref: null,
50 props: {
51 children: (value: T) => ReactNodeList,
52 - unstable_observedBits?: number,
52 ...
53 },
54 ...
@@ -59,7 +58,6 @@ export type ReactContext<T> = {
58 $$typeof: Symbol | number,
59 Consumer: ReactContext<T>,
60 Provider: ReactProviderType<T>,
62 - _calculateChangedBits: ((a: T, b: T) => number) | null,
61 _currentValue: T,
62 _currentValue2: T,
63 _threadCount: number,