@@ -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 ,
@@ -2069,6 +2120,7 @@ export const ContextOnlyDispatcher: Dispatcher = {
20692120
20702121 useCallback : throwInvalidHookError ,
20712122 useContext : throwInvalidHookError ,
2123+ useSelectedContext : throwInvalidHookError ,
20722124 useEffect : throwInvalidHookError ,
20732125 useImperativeHandle : throwInvalidHookError ,
20742126 useLayoutEffect : throwInvalidHookError ,
@@ -2094,6 +2146,7 @@ const HooksDispatcherOnMount: Dispatcher = {
20942146
20952147 useCallback : mountCallback ,
20962148 useContext : readContext ,
2149+ useSelectedContext : mountSelectedContext ,
20972150 useEffect : mountEffect ,
20982151 useImperativeHandle : mountImperativeHandle ,
20992152 useLayoutEffect : mountLayoutEffect ,
@@ -2119,6 +2172,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
21192172
21202173 useCallback : updateCallback ,
21212174 useContext : readContext ,
2175+ useSelectedContext : updateSelectedContext ,
21222176 useEffect : updateEffect ,
21232177 useImperativeHandle : updateImperativeHandle ,
21242178 useLayoutEffect : updateLayoutEffect ,
@@ -2144,6 +2198,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
21442198
21452199 useCallback : updateCallback ,
21462200 useContext : readContext ,
2201+ useSelectedContext : updateSelectedContext ,
21472202 useEffect : updateEffect ,
21482203 useImperativeHandle : updateImperativeHandle ,
21492204 useLayoutEffect : updateLayoutEffect ,
@@ -2212,6 +2267,21 @@ if (__DEV__) {
22122267 mountHookTypesDev ( ) ;
22132268 return readContext ( context , observedBits ) ;
22142269 } ,
2270+ useSelectedContext< C , S > (
2271+ context: ReactContext< C > ,
2272+ selector: C => S ,
2273+ isEqual : ( ( S , S ) => boolean ) | void ,
2274+ ) : S {
2275+ currentHookNameInDev = 'useSelectedContext' ;
2276+ mountHookTypesDev ( ) ;
2277+ const prevDispatcher = ReactCurrentDispatcher . current ;
2278+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2279+ try {
2280+ return mountSelectedContext ( context , selector , isEqual ) ;
2281+ } finally {
2282+ ReactCurrentDispatcher . current = prevDispatcher ;
2283+ }
2284+ } ,
22152285 useEffect (
22162286 create : ( ) => ( ( ) => void ) | void ,
22172287 deps : Array < mixed > | void | null,
@@ -2346,6 +2416,21 @@ if (__DEV__) {
23462416 updateHookTypesDev ( ) ;
23472417 return readContext ( context , observedBits ) ;
23482418 } ,
2419+ useSelectedContext< C , S > (
2420+ context: ReactContext< C > ,
2421+ selector: C => S ,
2422+ isEqual : ( ( S , S ) => boolean ) | void ,
2423+ ) : S {
2424+ currentHookNameInDev = 'useSelectedContext' ;
2425+ updateHookTypesDev ( ) ;
2426+ const prevDispatcher = ReactCurrentDispatcher . current ;
2427+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2428+ try {
2429+ return mountSelectedContext ( context , selector , isEqual ) ;
2430+ } finally {
2431+ ReactCurrentDispatcher . current = prevDispatcher ;
2432+ }
2433+ } ,
23492434 useEffect (
23502435 create : ( ) => ( ( ) => void ) | void ,
23512436 deps : Array < mixed > | void | null,
@@ -2476,6 +2561,21 @@ if (__DEV__) {
24762561 updateHookTypesDev ( ) ;
24772562 return readContext ( context , observedBits ) ;
24782563 } ,
2564+ useSelectedContext< C , S > (
2565+ context: ReactContext< C > ,
2566+ selector: C => S ,
2567+ isEqual : ( ( S , S ) => boolean ) | void ,
2568+ ) : S {
2569+ currentHookNameInDev = 'useSelectedContext' ;
2570+ updateHookTypesDev ( ) ;
2571+ const prevDispatcher = ReactCurrentDispatcher . current ;
2572+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
2573+ try {
2574+ return updateSelectedContext ( context , selector , isEqual ) ;
2575+ } finally {
2576+ ReactCurrentDispatcher . current = prevDispatcher ;
2577+ }
2578+ } ,
24792579 useEffect (
24802580 create : ( ) => ( ( ) => void ) | void ,
24812581 deps : Array < mixed > | void | null,
@@ -2607,6 +2707,21 @@ if (__DEV__) {
26072707 updateHookTypesDev ( ) ;
26082708 return readContext ( context , observedBits ) ;
26092709 } ,
2710+ useSelectedContext< C , S > (
2711+ context: ReactContext< C > ,
2712+ selector: C => S ,
2713+ isEqual : ( ( S , S ) => boolean ) | void ,
2714+ ) : S {
2715+ currentHookNameInDev = 'useSelectedContext' ;
2716+ updateHookTypesDev ( ) ;
2717+ const prevDispatcher = ReactCurrentDispatcher . current ;
2718+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnRerenderInDEV ;
2719+ try {
2720+ return updateSelectedContext ( context , selector , isEqual ) ;
2721+ } finally {
2722+ ReactCurrentDispatcher . current = prevDispatcher ;
2723+ }
2724+ } ,
26102725 useEffect (
26112726 create : ( ) => ( ( ) => void ) | void ,
26122727 deps : Array < mixed > | void | null,
@@ -2740,6 +2855,22 @@ if (__DEV__) {
27402855 mountHookTypesDev ( ) ;
27412856 return readContext ( context , observedBits ) ;
27422857 } ,
2858+ useSelectedContext< C , S > (
2859+ context: ReactContext< C > ,
2860+ selector: C => S ,
2861+ isEqual : ( ( S , S ) => boolean ) | void ,
2862+ ) : S {
2863+ currentHookNameInDev = 'useSelectedContext' ;
2864+ warnInvalidHookAccess ( ) ;
2865+ mountHookTypesDev ( ) ;
2866+ const prevDispatcher = ReactCurrentDispatcher . current ;
2867+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnMountInDEV ;
2868+ try {
2869+ return mountSelectedContext ( context , selector , isEqual ) ;
2870+ } finally {
2871+ ReactCurrentDispatcher . current = prevDispatcher ;
2872+ }
2873+ } ,
27432874 useEffect (
27442875 create : ( ) => ( ( ) => void ) | void ,
27452876 deps : Array < mixed > | void | null,
@@ -2885,6 +3016,22 @@ if (__DEV__) {
28853016 updateHookTypesDev ( ) ;
28863017 return readContext ( context , observedBits ) ;
28873018 } ,
3019+ useSelectedContext< C , S > (
3020+ context: ReactContext< C > ,
3021+ selector: C => S ,
3022+ isEqual : ( ( S , S ) => boolean ) | void ,
3023+ ) : S {
3024+ currentHookNameInDev = 'useSelectedContext' ;
3025+ warnInvalidHookAccess ( ) ;
3026+ updateHookTypesDev ( ) ;
3027+ const prevDispatcher = ReactCurrentDispatcher . current ;
3028+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
3029+ try {
3030+ return updateSelectedContext ( context , selector , isEqual ) ;
3031+ } finally {
3032+ ReactCurrentDispatcher . current = prevDispatcher ;
3033+ }
3034+ } ,
28883035 useEffect (
28893036 create : ( ) => ( ( ) => void ) | void ,
28903037 deps : Array < mixed > | void | null,
@@ -3031,6 +3178,22 @@ if (__DEV__) {
30313178 updateHookTypesDev ( ) ;
30323179 return readContext ( context , observedBits ) ;
30333180 } ,
3181+ useSelectedContext< C , S > (
3182+ context: ReactContext< C > ,
3183+ selector: C => S ,
3184+ isEqual : ( ( S , S ) => boolean ) | void ,
3185+ ) : S {
3186+ currentHookNameInDev = 'useSelectedContext' ;
3187+ warnInvalidHookAccess ( ) ;
3188+ updateHookTypesDev ( ) ;
3189+ const prevDispatcher = ReactCurrentDispatcher . current ;
3190+ ReactCurrentDispatcher . current = InvalidNestedHooksDispatcherOnUpdateInDEV ;
3191+ try {
3192+ return updateSelectedContext ( context , selector , isEqual ) ;
3193+ } finally {
3194+ ReactCurrentDispatcher . current = prevDispatcher ;
3195+ }
3196+ } ,
30343197 useEffect (
30353198 create : ( ) => ( ( ) => void ) | void ,
30363199 deps : Array < mixed > | void | null,
0 commit comments