@@ -30,6 +30,7 @@ import {
3030 decoupleUpdatePriorityFromScheduler ,
3131 enableUseRefAccessWarning ,
3232 enableDoubleInvokingEffects ,
33+ enableContextSelectors ,
3334} from 'shared/ReactFeatureFlags' ;
3435
3536import {
@@ -52,7 +53,7 @@ import {
5253 higherLanePriority ,
5354 DefaultLanePriority ,
5455} from './ReactFiberLane.new' ;
55- import { readContext } from './ReactFiberNewContext.new' ;
56+ import { readContext , readContextInsideHook } from './ReactFiberNewContext.new' ;
5657import { HostRoot , CacheComponent } from './ReactWorkTags' ;
5758import {
5859 Update as UpdateEffect ,
@@ -627,6 +628,56 @@ function updateWorkInProgressHook(): Hook {
627628 return workInProgressHook ;
628629}
629630
631+ function mountSelectedContext < C , S > (
632+ Context : ReactContext < C > ,
633+ selector : C => S ,
634+ isEqual : ( ( S , S ) => boolean ) | void ,
635+ ) : S {
636+ if ( ! enableContextSelectors ) {
637+ return ( undefined : any ) ;
638+ }
639+
640+ const hook = mountWorkInProgressHook ( ) ;
641+ const context = readContextInsideHook ( Context ) ;
642+ const selection = selector ( context ) ;
643+ hook . memoizedState = selection ;
644+ return selection ;
645+ }
646+
647+ function updateSelectedContext < C , S > (
648+ Context : ReactContext < C > ,
649+ selector : C => S ,
650+ isEqual : ( ( S , S ) => boolean ) | void ,
651+ ) : S {
652+ if ( ! enableContextSelectors ) {
653+ return ( undefined : any ) ;
654+ }
655+
656+ const hook = updateWorkInProgressHook ( ) ;
657+ const context = readContextInsideHook ( Context ) ;
658+ const newSelection = selector ( context ) ;
659+ const oldSelection : S = hook . memoizedState ;
660+ if ( isEqual !== undefined ) {
661+ if ( __DEV__ ) {
662+ if ( typeof isEqual !== 'function' ) {
663+ console . error (
664+ 'The optional third argument to useSelectedContext must be a ' +
665+ 'function. Instead got: %s' ,
666+ isEqual ,
667+ ) ;
668+ }
669+ }
670+ if ( isEqual ( newSelection , oldSelection ) ) {
671+ return oldSelection ;
672+ }
673+ } else if ( is ( newSelection , oldSelection ) ) {
674+ return oldSelection ;
675+ }
676+ markWorkInProgressReceivedUpdate ( ) ;
677+ hook . memoizedState = newSelection ;
678+ return newSelection ;
679+ }
680+
630681function createFunctionComponentUpdateQueue ( ) : FunctionComponentUpdateQueue {
631682 return {
632683 lastEffect : null ,
@@ -1995,6 +2046,7 @@ export const ContextOnlyDispatcher: Dispatcher = {
19952046
19962047 useCallback : throwInvalidHookError ,
19972048 useContext : throwInvalidHookError ,
2049+ useSelectedContext : throwInvalidHookError ,
19982050 useEffect : throwInvalidHookError ,
19992051 useImperativeHandle : throwInvalidHookError ,
20002052 useLayoutEffect : throwInvalidHookError ,
@@ -2020,6 +2072,7 @@ const HooksDispatcherOnMount: Dispatcher = {
20202072
20212073 useCallback : mountCallback ,
20222074 useContext : readContext ,
2075+ useSelectedContext : mountSelectedContext ,
20232076 useEffect : mountEffect ,
20242077 useImperativeHandle : mountImperativeHandle ,
20252078 useLayoutEffect : mountLayoutEffect ,
@@ -2045,6 +2098,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
20452098
20462099 useCallback : updateCallback ,
20472100 useContext : readContext ,
2101+ useSelectedContext : updateSelectedContext ,
20482102 useEffect : updateEffect ,
20492103 useImperativeHandle : updateImperativeHandle ,
20502104 useLayoutEffect : updateLayoutEffect ,
@@ -2070,6 +2124,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
20702124
20712125 useCallback : updateCallback ,
20722126 useContext : readContext ,
2127+ useSelectedContext : updateSelectedContext ,
20732128 useEffect : updateEffect ,
20742129 useImperativeHandle : updateImperativeHandle ,
20752130 useLayoutEffect : updateLayoutEffect ,
@@ -2138,6 +2193,21 @@ if (__DEV__) {
21382193 mountHookTypesDev ( ) ;
21392194 return readContext ( context , observedBits ) ;
21402195 } ,
2196+ useSelectedContext< C , S > (
2197+ context: ReactContext< C > ,
2198+ selector: C => S ,
2199+ isEqual : ( ( S , S ) => boolean ) | void ,
2200+ ) : S {
2201+ currentHookNameInDev = 'useSelectedContext' ;
2202+ mountHookTypesDev ( ) ;
2203+ const prevDispatcher = ReactCurrentDispatcher . current ;
2204+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2205+ try {
2206+ return mountSelectedContext ( context , selector , isEqual ) ;
2207+ } finally {
2208+ ReactCurrentDispatcher . current = prevDispatcher ;
2209+ }
2210+ } ,
21412211 useEffect (
21422212 create : ( ) => ( ( ) => void ) | void ,
21432213 deps : Array < mixed > | void | null,
@@ -2272,6 +2342,21 @@ if (__DEV__) {
22722342 updateHookTypesDev ( ) ;
22732343 return readContext ( context , observedBits ) ;
22742344 } ,
2345+ useSelectedContext< C , S > (
2346+ context: ReactContext< C > ,
2347+ selector: C => S ,
2348+ isEqual : ( ( S , S ) => boolean ) | void ,
2349+ ) : S {
2350+ currentHookNameInDev = 'useSelectedContext' ;
2351+ updateHookTypesDev ( ) ;
2352+ const prevDispatcher = ReactCurrentDispatcher . current ;
2353+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2354+ try {
2355+ return mountSelectedContext ( context , selector , isEqual ) ;
2356+ } finally {
2357+ ReactCurrentDispatcher . current = prevDispatcher ;
2358+ }
2359+ } ,
22752360 useEffect (
22762361 create : ( ) => ( ( ) => void ) | void ,
22772362 deps : Array < mixed > | void | null,
@@ -2402,6 +2487,21 @@ if (__DEV__) {
24022487 updateHookTypesDev ( ) ;
24032488 return readContext ( context , observedBits ) ;
24042489 } ,
2490+ useSelectedContext< C , S > (
2491+ context: ReactContext< C > ,
2492+ selector: C => S ,
2493+ isEqual : ( ( S , S ) => boolean ) | void ,
2494+ ) : S {
2495+ currentHookNameInDev = 'useSelectedContext' ;
2496+ updateHookTypesDev ( ) ;
2497+ const prevDispatcher = ReactCurrentDispatcher . current ;
2498+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
2499+ try {
2500+ return updateSelectedContext ( context , selector , isEqual ) ;
2501+ } finally {
2502+ ReactCurrentDispatcher . current = prevDispatcher ;
2503+ }
2504+ } ,
24052505 useEffect (
24062506 create : ( ) => ( ( ) => void ) | void ,
24072507 deps : Array < mixed > | void | null,
@@ -2533,6 +2633,21 @@ if (__DEV__) {
25332633 updateHookTypesDev ( ) ;
25342634 return readContext ( context , observedBits ) ;
25352635 } ,
2636+ useSelectedContext< C , S > (
2637+ context: ReactContext< C > ,
2638+ selector: C => S ,
2639+ isEqual : ( ( S , S ) => boolean ) | void ,
2640+ ) : S {
2641+ currentHookNameInDev = 'useSelectedContext' ;
2642+ updateHookTypesDev ( ) ;
2643+ const prevDispatcher = ReactCurrentDispatcher . current ;
2644+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnRerenderInDEV ;
2645+ try {
2646+ return updateSelectedContext ( context , selector , isEqual ) ;
2647+ } finally {
2648+ ReactCurrentDispatcher . current = prevDispatcher ;
2649+ }
2650+ } ,
25362651 useEffect (
25372652 create : ( ) => ( ( ) => void ) | void ,
25382653 deps : Array < mixed > | void | null,
@@ -2666,6 +2781,22 @@ if (__DEV__) {
26662781 mountHookTypesDev ( ) ;
26672782 return readContext ( context , observedBits ) ;
26682783 } ,
2784+ useSelectedContext< C , S > (
2785+ context: ReactContext< C > ,
2786+ selector: C => S ,
2787+ isEqual : ( ( S , S ) => boolean ) | void ,
2788+ ) : S {
2789+ currentHookNameInDev = 'useSelectedContext' ;
2790+ warnInvalidHookAccess ( ) ;
2791+ mountHookTypesDev ( ) ;
2792+ const prevDispatcher = ReactCurrentDispatcher . current ;
2793+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2794+ try {
2795+ return mountSelectedContext ( context , selector , isEqual ) ;
2796+ } finally {
2797+ ReactCurrentDispatcher . current = prevDispatcher ;
2798+ }
2799+ } ,
26692800 useEffect (
26702801 create : ( ) => ( ( ) => void ) | void ,
26712802 deps : Array < mixed > | void | null,
@@ -2811,6 +2942,22 @@ if (__DEV__) {
28112942 updateHookTypesDev ( ) ;
28122943 return readContext ( context , observedBits ) ;
28132944 } ,
2945+ useSelectedContext< C , S > (
2946+ context: ReactContext< C > ,
2947+ selector: C => S ,
2948+ isEqual : ( ( S , S ) => boolean ) | void ,
2949+ ) : S {
2950+ currentHookNameInDev = 'useSelectedContext' ;
2951+ warnInvalidHookAccess ( ) ;
2952+ updateHookTypesDev ( ) ;
2953+ const prevDispatcher = ReactCurrentDispatcher . current ;
2954+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
2955+ try {
2956+ return updateSelectedContext ( context , selector , isEqual ) ;
2957+ } finally {
2958+ ReactCurrentDispatcher . current = prevDispatcher ;
2959+ }
2960+ } ,
28142961 useEffect (
28152962 create : ( ) => ( ( ) => void ) | void ,
28162963 deps : Array < mixed > | void | null,
@@ -2957,6 +3104,22 @@ if (__DEV__) {
29573104 updateHookTypesDev ( ) ;
29583105 return readContext ( context , observedBits ) ;
29593106 } ,
3107+ useSelectedContext< C , S > (
3108+ context: ReactContext< C > ,
3109+ selector: C => S ,
3110+ isEqual : ( ( S , S ) => boolean ) | void ,
3111+ ) : S {
3112+ currentHookNameInDev = 'useSelectedContext' ;
3113+ warnInvalidHookAccess ( ) ;
3114+ updateHookTypesDev ( ) ;
3115+ const prevDispatcher = ReactCurrentDispatcher . current ;
3116+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
3117+ try {
3118+ return updateSelectedContext ( context , selector , isEqual ) ;
3119+ } finally {
3120+ ReactCurrentDispatcher . current = prevDispatcher ;
3121+ }
3122+ } ,
29603123 useEffect (
29613124 create : ( ) => ( ( ) => void ) | void ,
29623125 deps : Array < mixed > | void | null,
0 commit comments