@@ -30,6 +30,7 @@ import {
3030 decoupleUpdatePriorityFromScheduler ,
3131 enableUseRefAccessWarning ,
3232 enableDoubleInvokingEffects ,
33+ enableContextSelectors ,
3334} from 'shared/ReactFeatureFlags' ;
3435
3536import {
@@ -54,7 +55,7 @@ import {
5455 higherLanePriority ,
5556 DefaultLanePriority ,
5657} from './ReactFiberLane.new' ;
57- import { readContext } from './ReactFiberNewContext.new' ;
58+ import { readContext , readContextInsideHook } from './ReactFiberNewContext.new' ;
5859import { HostRoot , CacheComponent } from './ReactWorkTags' ;
5960import {
6061 Update as UpdateEffect ,
@@ -634,6 +635,56 @@ function updateWorkInProgressHook(): Hook {
634635 return workInProgressHook ;
635636}
636637
638+ function mountSelectedContext < C , S > (
639+ Context : ReactContext < C > ,
640+ selector : C => S ,
641+ isEqual : ( ( S , S ) => boolean ) | void ,
642+ ) : S {
643+ if ( ! enableContextSelectors ) {
644+ return ( undefined : any ) ;
645+ }
646+
647+ const hook = mountWorkInProgressHook ( ) ;
648+ const context = readContextInsideHook ( Context ) ;
649+ const selection = selector ( context ) ;
650+ hook . memoizedState = selection ;
651+ return selection ;
652+ }
653+
654+ function updateSelectedContext < C , S > (
655+ Context : ReactContext < C > ,
656+ selector : C => S ,
657+ isEqual : ( ( S , S ) => boolean ) | void ,
658+ ) : S {
659+ if ( ! enableContextSelectors ) {
660+ return ( undefined : any ) ;
661+ }
662+
663+ const hook = updateWorkInProgressHook ( ) ;
664+ const context = readContextInsideHook ( Context ) ;
665+ const newSelection = selector ( context ) ;
666+ const oldSelection : S = hook . memoizedState ;
667+ if ( isEqual !== undefined ) {
668+ if ( __DEV__ ) {
669+ if ( typeof isEqual !== 'function' ) {
670+ console . error (
671+ 'The optional third argument to useSelectedContext must be a ' +
672+ 'function. Instead got: %s' ,
673+ isEqual ,
674+ ) ;
675+ }
676+ }
677+ if ( isEqual ( newSelection , oldSelection ) ) {
678+ return oldSelection ;
679+ }
680+ } else if ( is ( newSelection , oldSelection ) ) {
681+ return oldSelection ;
682+ }
683+ markWorkInProgressReceivedUpdate ( ) ;
684+ hook . memoizedState = newSelection ;
685+ return newSelection ;
686+ }
687+
637688function createFunctionComponentUpdateQueue ( ) : FunctionComponentUpdateQueue {
638689 return {
639690 lastEffect : null ,
@@ -2068,6 +2119,7 @@ export const ContextOnlyDispatcher: Dispatcher = {
20682119
20692120 useCallback : throwInvalidHookError ,
20702121 useContext : throwInvalidHookError ,
2122+ useSelectedContext : throwInvalidHookError ,
20712123 useEffect : throwInvalidHookError ,
20722124 useImperativeHandle : throwInvalidHookError ,
20732125 useLayoutEffect : throwInvalidHookError ,
@@ -2093,6 +2145,7 @@ const HooksDispatcherOnMount: Dispatcher = {
20932145
20942146 useCallback : mountCallback ,
20952147 useContext : readContext ,
2148+ useSelectedContext : mountSelectedContext ,
20962149 useEffect : mountEffect ,
20972150 useImperativeHandle : mountImperativeHandle ,
20982151 useLayoutEffect : mountLayoutEffect ,
@@ -2118,6 +2171,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
21182171
21192172 useCallback : updateCallback ,
21202173 useContext : readContext ,
2174+ useSelectedContext : updateSelectedContext ,
21212175 useEffect : updateEffect ,
21222176 useImperativeHandle : updateImperativeHandle ,
21232177 useLayoutEffect : updateLayoutEffect ,
@@ -2143,6 +2197,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
21432197
21442198 useCallback : updateCallback ,
21452199 useContext : readContext ,
2200+ useSelectedContext : updateSelectedContext ,
21462201 useEffect : updateEffect ,
21472202 useImperativeHandle : updateImperativeHandle ,
21482203 useLayoutEffect : updateLayoutEffect ,
@@ -2211,6 +2266,21 @@ if (__DEV__) {
22112266 mountHookTypesDev ( ) ;
22122267 return readContext ( context , observedBits ) ;
22132268 } ,
2269+ useSelectedContext< C , S > (
2270+ context: ReactContext< C > ,
2271+ selector: C => S ,
2272+ isEqual : ( ( S , S ) => boolean ) | void ,
2273+ ) : S {
2274+ currentHookNameInDev = 'useSelectedContext' ;
2275+ mountHookTypesDev ( ) ;
2276+ const prevDispatcher = ReactCurrentDispatcher . current ;
2277+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2278+ try {
2279+ return mountSelectedContext ( context , selector , isEqual ) ;
2280+ } finally {
2281+ ReactCurrentDispatcher . current = prevDispatcher ;
2282+ }
2283+ } ,
22142284 useEffect (
22152285 create : ( ) => ( ( ) => void ) | void ,
22162286 deps : Array < mixed > | void | null,
@@ -2345,6 +2415,21 @@ if (__DEV__) {
23452415 updateHookTypesDev ( ) ;
23462416 return readContext ( context , observedBits ) ;
23472417 } ,
2418+ useSelectedContext< C , S > (
2419+ context: ReactContext< C > ,
2420+ selector: C => S ,
2421+ isEqual : ( ( S , S ) => boolean ) | void ,
2422+ ) : S {
2423+ currentHookNameInDev = 'useSelectedContext' ;
2424+ updateHookTypesDev ( ) ;
2425+ const prevDispatcher = ReactCurrentDispatcher . current ;
2426+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2427+ try {
2428+ return mountSelectedContext ( context , selector , isEqual ) ;
2429+ } finally {
2430+ ReactCurrentDispatcher . current = prevDispatcher ;
2431+ }
2432+ } ,
23482433 useEffect (
23492434 create : ( ) => ( ( ) => void ) | void ,
23502435 deps : Array < mixed > | void | null,
@@ -2475,6 +2560,21 @@ if (__DEV__) {
24752560 updateHookTypesDev ( ) ;
24762561 return readContext ( context , observedBits ) ;
24772562 } ,
2563+ useSelectedContext< C , S > (
2564+ context: ReactContext< C > ,
2565+ selector: C => S ,
2566+ isEqual : ( ( S , S ) => boolean ) | void ,
2567+ ) : S {
2568+ currentHookNameInDev = 'useSelectedContext' ;
2569+ updateHookTypesDev ( ) ;
2570+ const prevDispatcher = ReactCurrentDispatcher . current ;
2571+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
2572+ try {
2573+ return updateSelectedContext ( context , selector , isEqual ) ;
2574+ } finally {
2575+ ReactCurrentDispatcher . current = prevDispatcher ;
2576+ }
2577+ } ,
24782578 useEffect (
24792579 create : ( ) => ( ( ) => void ) | void ,
24802580 deps : Array < mixed > | void | null,
@@ -2606,6 +2706,21 @@ if (__DEV__) {
26062706 updateHookTypesDev ( ) ;
26072707 return readContext ( context , observedBits ) ;
26082708 } ,
2709+ useSelectedContext< C , S > (
2710+ context: ReactContext< C > ,
2711+ selector: C => S ,
2712+ isEqual : ( ( S , S ) => boolean ) | void ,
2713+ ) : S {
2714+ currentHookNameInDev = 'useSelectedContext' ;
2715+ updateHookTypesDev ( ) ;
2716+ const prevDispatcher = ReactCurrentDispatcher . current ;
2717+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnRerenderInDEV ;
2718+ try {
2719+ return updateSelectedContext ( context , selector , isEqual ) ;
2720+ } finally {
2721+ ReactCurrentDispatcher . current = prevDispatcher ;
2722+ }
2723+ } ,
26092724 useEffect (
26102725 create : ( ) => ( ( ) => void ) | void ,
26112726 deps : Array < mixed > | void | null,
@@ -2739,6 +2854,22 @@ if (__DEV__) {
27392854 mountHookTypesDev ( ) ;
27402855 return readContext ( context , observedBits ) ;
27412856 } ,
2857+ useSelectedContext< C , S > (
2858+ context: ReactContext< C > ,
2859+ selector: C => S ,
2860+ isEqual : ( ( S , S ) => boolean ) | void ,
2861+ ) : S {
2862+ currentHookNameInDev = 'useSelectedContext' ;
2863+ warnInvalidHookAccess ( ) ;
2864+ mountHookTypesDev ( ) ;
2865+ const prevDispatcher = ReactCurrentDispatcher . current ;
2866+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2867+ try {
2868+ return mountSelectedContext ( context , selector , isEqual ) ;
2869+ } finally {
2870+ ReactCurrentDispatcher . current = prevDispatcher ;
2871+ }
2872+ } ,
27422873 useEffect (
27432874 create : ( ) => ( ( ) => void ) | void ,
27442875 deps : Array < mixed > | void | null,
@@ -2884,6 +3015,22 @@ if (__DEV__) {
28843015 updateHookTypesDev ( ) ;
28853016 return readContext ( context , observedBits ) ;
28863017 } ,
3018+ useSelectedContext< C , S > (
3019+ context: ReactContext< C > ,
3020+ selector: C => S ,
3021+ isEqual : ( ( S , S ) => boolean ) | void ,
3022+ ) : S {
3023+ currentHookNameInDev = 'useSelectedContext' ;
3024+ warnInvalidHookAccess ( ) ;
3025+ updateHookTypesDev ( ) ;
3026+ const prevDispatcher = ReactCurrentDispatcher . current ;
3027+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
3028+ try {
3029+ return updateSelectedContext ( context , selector , isEqual ) ;
3030+ } finally {
3031+ ReactCurrentDispatcher . current = prevDispatcher ;
3032+ }
3033+ } ,
28873034 useEffect (
28883035 create : ( ) => ( ( ) => void ) | void ,
28893036 deps : Array < mixed > | void | null,
@@ -3030,6 +3177,22 @@ if (__DEV__) {
30303177 updateHookTypesDev ( ) ;
30313178 return readContext ( context , observedBits ) ;
30323179 } ,
3180+ useSelectedContext< C , S > (
3181+ context: ReactContext< C > ,
3182+ selector: C => S ,
3183+ isEqual : ( ( S , S ) => boolean ) | void ,
3184+ ) : S {
3185+ currentHookNameInDev = 'useSelectedContext' ;
3186+ warnInvalidHookAccess ( ) ;
3187+ updateHookTypesDev ( ) ;
3188+ const prevDispatcher = ReactCurrentDispatcher . current ;
3189+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
3190+ try {
3191+ return updateSelectedContext ( context , selector , isEqual ) ;
3192+ } finally {
3193+ ReactCurrentDispatcher . current = prevDispatcher ;
3194+ }
3195+ } ,
30333196 useEffect (
30343197 create : ( ) => ( ( ) => void ) | void ,
30353198 deps : Array < mixed > | void | null,
0 commit comments