Skip to content

Commit

Permalink
Move API to unstable useContext option
Browse files Browse the repository at this point in the history
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;
}
```
  • Loading branch information
acdlite committed Jul 8, 2021
1 parent c3ad977 commit c0522ec
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 285 deletions.
18 changes: 4 additions & 14 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ function readContext<T>(context: ReactContext<T>): T {
return context._currentValue;
}

function useContext<T>(context: ReactContext<T>): T {
function useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
hookLog.push({
primitive: 'Context',
stackError: new Error(),
Expand All @@ -123,18 +126,6 @@ function useContext<T>(context: ReactContext<T>): T {
return context._currentValue;
}

function useContextSelector<C, S>(
context: ReactContext<C>,
selector: C => S,
): C {
hookLog.push({
primitive: 'ContextSelector',
stackError: new Error(),
value: context._currentValue,
});
return context._currentValue;
}

function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
Expand Down Expand Up @@ -328,7 +319,6 @@ const Dispatcher: DispatcherType = {
useCacheRefresh,
useCallback,
useContext,
useContextSelector,
useEffect,
useImperativeHandle,
useDebugValue,
Expand Down
19 changes: 4 additions & 15 deletions packages/react-dom/src/server/ReactPartialRendererHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,10 @@ function readContext<T>(context: ReactContext<T>): T {
return context[threadID];
}

function useContext<T>(context: ReactContext<T>): T {
function useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
if (__DEV__) {
currentHookNameInDev = 'useContext';
}
Expand All @@ -245,19 +248,6 @@ function useContext<T>(context: ReactContext<T>): T {
return context[threadID];
}

function useContextSelector<C, S>(
context: ReactContext<C>,
selector: C => S,
): C {
if (__DEV__) {
currentHookNameInDev = 'useContextSelector';
}
resolveCurrentlyRenderingComponent();
const threadID = currentPartialRenderer.threadID;
validateContextBounds(context, threadID);
return context[threadID];
}

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
Expand Down Expand Up @@ -510,7 +500,6 @@ export function setCurrentPartialRenderer(renderer: PartialRenderer) {
export const Dispatcher: DispatcherType = {
readContext,
useContext,
useContextSelector,
useMemo,
useReducer,
useRef,
Expand Down
152 changes: 51 additions & 101 deletions packages/react-reconciler/src/ReactFiberHooks.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,19 @@ function updateWorkInProgressHook(): Hook {
return workInProgressHook;
}

function useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
if (options !== undefined) {
const selector = options.unstable_selector;
if (selector !== undefined) {
return readContextWithSelector(context, selector);
}
}
return readContext(context);
}

function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
return {
lastEffect: null,
Expand Down Expand Up @@ -2071,7 +2084,6 @@ export const ContextOnlyDispatcher: Dispatcher = {

useCallback: throwInvalidHookError,
useContext: throwInvalidHookError,
useContextSelector: throwInvalidHookError,
useEffect: throwInvalidHookError,
useImperativeHandle: throwInvalidHookError,
useLayoutEffect: throwInvalidHookError,
Expand All @@ -2096,8 +2108,7 @@ const HooksDispatcherOnMount: Dispatcher = {
readContext,

useCallback: mountCallback,
useContext: readContext,
useContextSelector: readContextWithSelector,
useContext: useContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
Expand All @@ -2122,8 +2133,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
readContext,

useCallback: updateCallback,
useContext: readContext,
useContextSelector: readContextWithSelector,
useContext: useContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
Expand All @@ -2148,8 +2158,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
readContext,

useCallback: updateCallback,
useContext: readContext,
useContextSelector: readContextWithSelector,
useContext: useContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
Expand Down Expand Up @@ -2207,21 +2216,13 @@ if (__DEV__) {
checkDepsAreArrayDev(deps);
return mountCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
mountHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
mountHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -2346,21 +2347,13 @@ if (__DEV__) {
updateHookTypesDev();
return mountCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
updateHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -2481,21 +2474,13 @@ if (__DEV__) {
updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
updateHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -2617,21 +2602,13 @@ if (__DEV__) {
updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
updateHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -2754,23 +2731,14 @@ if (__DEV__) {
mountHookTypesDev();
return mountCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
warnInvalidHookAccess();
mountHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
warnInvalidHookAccess();
mountHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -2905,23 +2873,14 @@ if (__DEV__) {
updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
warnInvalidHookAccess();
updateHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
warnInvalidHookAccess();
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down Expand Up @@ -3057,23 +3016,14 @@ if (__DEV__) {
updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext<T>(context: ReactContext<T>): T {
useContext<T, S>(
context: ReactContext<T>,
options?: {unstable_selector?: T => S},
): T {
currentHookNameInDev = 'useContext';
warnInvalidHookAccess();
updateHookTypesDev();
return readContext(context);
},
useContextSelector<C, S>(context: ReactContext<C>, selector: C => S): C {
currentHookNameInDev = 'useContextSelector';
warnInvalidHookAccess();
updateHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return readContextWithSelector(context, selector);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
return useContext(context, options);
},
useEffect(
create: () => (() => void) | void,
Expand Down
Loading

0 comments on commit c0522ec

Please sign in to comment.