Skip to content

Commit 85dc024

Browse files
committed
Move API to unstable useContext option
This will to make it easier to A/B test, or to revert if we abandon the experiment. Using a selector will not change the return type of `useContext`. Use a userspace hook to get the selected value: ```js function useContextSelector<C, S>(Context: C, selector: C => S): S { const context = useContext(Context, {unstable_selector: selector}); const selectedContext = selector(context); return selectedContext; } ```
1 parent ae128b3 commit 85dc024

15 files changed

Lines changed: 123 additions & 285 deletions

File tree

packages/react-debug-tools/src/ReactDebugHooks.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ function readContext<T>(context: ReactContext<T>): T {
114114
return context._currentValue;
115115
}
116116

117-
function useContext<T>(context: ReactContext<T>): T {
117+
function useContext<T, S>(
118+
context: ReactContext<T>,
119+
options?: {unstable_selector?: T => S},
120+
): T {
118121
hookLog.push({
119122
primitive: 'Context',
120123
stackError: new Error(),
@@ -123,18 +126,6 @@ function useContext<T>(context: ReactContext<T>): T {
123126
return context._currentValue;
124127
}
125128

126-
function useContextSelector<C, S>(
127-
context: ReactContext<C>,
128-
selector: C => S,
129-
): C {
130-
hookLog.push({
131-
primitive: 'ContextSelector',
132-
stackError: new Error(),
133-
value: context._currentValue,
134-
});
135-
return context._currentValue;
136-
}
137-
138129
function useState<S>(
139130
initialState: (() => S) | S,
140131
): [S, Dispatch<BasicStateAction<S>>] {
@@ -328,7 +319,6 @@ const Dispatcher: DispatcherType = {
328319
useCacheRefresh,
329320
useCallback,
330321
useContext,
331-
useContextSelector,
332322
useEffect,
333323
useImperativeHandle,
334324
useDebugValue,

packages/react-dom/src/server/ReactPartialRendererHooks.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,10 @@ function readContext<T>(context: ReactContext<T>): T {
235235
return context[threadID];
236236
}
237237

238-
function useContext<T>(context: ReactContext<T>): T {
238+
function useContext<T, S>(
239+
context: ReactContext<T>,
240+
options?: {unstable_selector?: T => S},
241+
): T {
239242
if (__DEV__) {
240243
currentHookNameInDev = 'useContext';
241244
}
@@ -245,19 +248,6 @@ function useContext<T>(context: ReactContext<T>): T {
245248
return context[threadID];
246249
}
247250

248-
function useContextSelector<C, S>(
249-
context: ReactContext<C>,
250-
selector: C => S,
251-
): C {
252-
if (__DEV__) {
253-
currentHookNameInDev = 'useContextSelector';
254-
}
255-
resolveCurrentlyRenderingComponent();
256-
const threadID = currentPartialRenderer.threadID;
257-
validateContextBounds(context, threadID);
258-
return context[threadID];
259-
}
260-
261251
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
262252
// $FlowFixMe: Flow doesn't like mixed types
263253
return typeof action === 'function' ? action(state) : action;
@@ -510,7 +500,6 @@ export function setCurrentPartialRenderer(renderer: PartialRenderer) {
510500
export const Dispatcher: DispatcherType = {
511501
readContext,
512502
useContext,
513-
useContextSelector,
514503
useMemo,
515504
useReducer,
516505
useRef,

packages/react-reconciler/src/ReactFiberHooks.new.js

Lines changed: 51 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,19 @@ function updateWorkInProgressHook(): Hook {
680680
return workInProgressHook;
681681
}
682682

683+
function useContext<T, S>(
684+
context: ReactContext<T>,
685+
options?: {unstable_selector?: T => S},
686+
): T {
687+
if (options !== undefined) {
688+
const selector = options.unstable_selector;
689+
if (selector !== undefined) {
690+
return readContextWithSelector(context, selector);
691+
}
692+
}
693+
return readContext(context);
694+
}
695+
683696
function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
684697
return {
685698
lastEffect: null,
@@ -2071,7 +2084,6 @@ export const ContextOnlyDispatcher: Dispatcher = {
20712084

20722085
useCallback: throwInvalidHookError,
20732086
useContext: throwInvalidHookError,
2074-
useContextSelector: throwInvalidHookError,
20752087
useEffect: throwInvalidHookError,
20762088
useImperativeHandle: throwInvalidHookError,
20772089
useLayoutEffect: throwInvalidHookError,
@@ -2096,8 +2108,7 @@ const HooksDispatcherOnMount: Dispatcher = {
20962108
readContext,
20972109

20982110
useCallback: mountCallback,
2099-
useContext: readContext,
2100-
useContextSelector: readContextWithSelector,
2111+
useContext: useContext,
21012112
useEffect: mountEffect,
21022113
useImperativeHandle: mountImperativeHandle,
21032114
useLayoutEffect: mountLayoutEffect,
@@ -2122,8 +2133,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
21222133
readContext,
21232134

21242135
useCallback: updateCallback,
2125-
useContext: readContext,
2126-
useContextSelector: readContextWithSelector,
2136+
useContext: useContext,
21272137
useEffect: updateEffect,
21282138
useImperativeHandle: updateImperativeHandle,
21292139
useLayoutEffect: updateLayoutEffect,
@@ -2148,8 +2158,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
21482158
readContext,
21492159

21502160
useCallback: updateCallback,
2151-
useContext: readContext,
2152-
useContextSelector: readContextWithSelector,
2161+
useContext: useContext,
21532162
useEffect: updateEffect,
21542163
useImperativeHandle: updateImperativeHandle,
21552164
useLayoutEffect: updateLayoutEffect,
@@ -2207,21 +2216,13 @@ if (__DEV__) {
22072216
checkDepsAreArrayDev(deps);
22082217
return mountCallback(callback, deps);
22092218
},
2210-
useContext<T>(context: ReactContext<T>): T {
2219+
useContext<T, S>(
2220+
context: ReactContext<T>,
2221+
options?: {unstable_selector?: T => S},
2222+
): T {
22112223
currentHookNameInDev = 'useContext';
22122224
mountHookTypesDev();
2213-
return readContext(context);
2214-
},
2215-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2216-
currentHookNameInDev = 'useContextSelector';
2217-
mountHookTypesDev();
2218-
const prevDispatcher = ReactCurrentDispatcher.current;
2219-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
2220-
try {
2221-
return readContextWithSelector(context, selector);
2222-
} finally {
2223-
ReactCurrentDispatcher.current = prevDispatcher;
2224-
}
2225+
return useContext(context, options);
22252226
},
22262227
useEffect(
22272228
create: () => (() => void) | void,
@@ -2346,21 +2347,13 @@ if (__DEV__) {
23462347
updateHookTypesDev();
23472348
return mountCallback(callback, deps);
23482349
},
2349-
useContext<T>(context: ReactContext<T>): T {
2350+
useContext<T, S>(
2351+
context: ReactContext<T>,
2352+
options?: {unstable_selector?: T => S},
2353+
): T {
23502354
currentHookNameInDev = 'useContext';
23512355
updateHookTypesDev();
2352-
return readContext(context);
2353-
},
2354-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2355-
currentHookNameInDev = 'useContextSelector';
2356-
updateHookTypesDev();
2357-
const prevDispatcher = ReactCurrentDispatcher.current;
2358-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
2359-
try {
2360-
return readContextWithSelector(context, selector);
2361-
} finally {
2362-
ReactCurrentDispatcher.current = prevDispatcher;
2363-
}
2356+
return useContext(context, options);
23642357
},
23652358
useEffect(
23662359
create: () => (() => void) | void,
@@ -2481,21 +2474,13 @@ if (__DEV__) {
24812474
updateHookTypesDev();
24822475
return updateCallback(callback, deps);
24832476
},
2484-
useContext<T>(context: ReactContext<T>): T {
2477+
useContext<T, S>(
2478+
context: ReactContext<T>,
2479+
options?: {unstable_selector?: T => S},
2480+
): T {
24852481
currentHookNameInDev = 'useContext';
24862482
updateHookTypesDev();
2487-
return readContext(context);
2488-
},
2489-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2490-
currentHookNameInDev = 'useContextSelector';
2491-
updateHookTypesDev();
2492-
const prevDispatcher = ReactCurrentDispatcher.current;
2493-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
2494-
try {
2495-
return readContextWithSelector(context, selector);
2496-
} finally {
2497-
ReactCurrentDispatcher.current = prevDispatcher;
2498-
}
2483+
return useContext(context, options);
24992484
},
25002485
useEffect(
25012486
create: () => (() => void) | void,
@@ -2617,21 +2602,13 @@ if (__DEV__) {
26172602
updateHookTypesDev();
26182603
return updateCallback(callback, deps);
26192604
},
2620-
useContext<T>(context: ReactContext<T>): T {
2605+
useContext<T, S>(
2606+
context: ReactContext<T>,
2607+
options?: {unstable_selector?: T => S},
2608+
): T {
26212609
currentHookNameInDev = 'useContext';
26222610
updateHookTypesDev();
2623-
return readContext(context);
2624-
},
2625-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2626-
currentHookNameInDev = 'useContextSelector';
2627-
updateHookTypesDev();
2628-
const prevDispatcher = ReactCurrentDispatcher.current;
2629-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
2630-
try {
2631-
return readContextWithSelector(context, selector);
2632-
} finally {
2633-
ReactCurrentDispatcher.current = prevDispatcher;
2634-
}
2611+
return useContext(context, options);
26352612
},
26362613
useEffect(
26372614
create: () => (() => void) | void,
@@ -2754,23 +2731,14 @@ if (__DEV__) {
27542731
mountHookTypesDev();
27552732
return mountCallback(callback, deps);
27562733
},
2757-
useContext<T>(context: ReactContext<T>): T {
2734+
useContext<T, S>(
2735+
context: ReactContext<T>,
2736+
options?: {unstable_selector?: T => S},
2737+
): T {
27582738
currentHookNameInDev = 'useContext';
27592739
warnInvalidHookAccess();
27602740
mountHookTypesDev();
2761-
return readContext(context);
2762-
},
2763-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2764-
currentHookNameInDev = 'useContextSelector';
2765-
warnInvalidHookAccess();
2766-
mountHookTypesDev();
2767-
const prevDispatcher = ReactCurrentDispatcher.current;
2768-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
2769-
try {
2770-
return readContextWithSelector(context, selector);
2771-
} finally {
2772-
ReactCurrentDispatcher.current = prevDispatcher;
2773-
}
2741+
return useContext(context, options);
27742742
},
27752743
useEffect(
27762744
create: () => (() => void) | void,
@@ -2905,23 +2873,14 @@ if (__DEV__) {
29052873
updateHookTypesDev();
29062874
return updateCallback(callback, deps);
29072875
},
2908-
useContext<T>(context: ReactContext<T>): T {
2876+
useContext<T, S>(
2877+
context: ReactContext<T>,
2878+
options?: {unstable_selector?: T => S},
2879+
): T {
29092880
currentHookNameInDev = 'useContext';
29102881
warnInvalidHookAccess();
29112882
updateHookTypesDev();
2912-
return readContext(context);
2913-
},
2914-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
2915-
currentHookNameInDev = 'useContextSelector';
2916-
warnInvalidHookAccess();
2917-
updateHookTypesDev();
2918-
const prevDispatcher = ReactCurrentDispatcher.current;
2919-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
2920-
try {
2921-
return readContextWithSelector(context, selector);
2922-
} finally {
2923-
ReactCurrentDispatcher.current = prevDispatcher;
2924-
}
2883+
return useContext(context, options);
29252884
},
29262885
useEffect(
29272886
create: () => (() => void) | void,
@@ -3057,23 +3016,14 @@ if (__DEV__) {
30573016
updateHookTypesDev();
30583017
return updateCallback(callback, deps);
30593018
},
3060-
useContext<T>(context: ReactContext<T>): T {
3019+
useContext<T, S>(
3020+
context: ReactContext<T>,
3021+
options?: {unstable_selector?: T => S},
3022+
): T {
30613023
currentHookNameInDev = 'useContext';
30623024
warnInvalidHookAccess();
30633025
updateHookTypesDev();
3064-
return readContext(context);
3065-
},
3066-
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
3067-
currentHookNameInDev = 'useContextSelector';
3068-
warnInvalidHookAccess();
3069-
updateHookTypesDev();
3070-
const prevDispatcher = ReactCurrentDispatcher.current;
3071-
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
3072-
try {
3073-
return readContextWithSelector(context, selector);
3074-
} finally {
3075-
ReactCurrentDispatcher.current = prevDispatcher;
3076-
}
3026+
return useContext(context, options);
30773027
},
30783028
useEffect(
30793029
create: () => (() => void) | void,

0 commit comments

Comments
 (0)