1
- import * as React from 'react' ;
2
- import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect' ;
3
1
import useEvent from 'rc-util/lib/hooks/useEvent' ;
2
+ import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect' ;
3
+ import * as React from 'react' ;
4
+ import { unstable_batchedUpdates } from 'react-dom' ;
4
5
import shallowEqual from 'shallowequal' ;
5
6
6
7
export type Selector < T , O = T > = ( value : T ) => O ;
@@ -24,8 +25,8 @@ export interface ReturnCreateContext<T> {
24
25
Provider : React . ComponentType < ContextSelectorProviderProps < T > > ;
25
26
}
26
27
27
- export function createContext < T > ( ) : ReturnCreateContext < T > {
28
- const Context = React . createContext < Context < T > > ( null as any ) ;
28
+ export function createContext < T > ( defaultContext ?: T ) : ReturnCreateContext < T > {
29
+ const Context = React . createContext < Context < T > > ( defaultContext as any ) ;
29
30
30
31
const Provider = ( { value, children } : ContextSelectorProviderProps < T > ) => {
31
32
const valueRef = React . useRef ( value ) ;
@@ -37,8 +38,10 @@ export function createContext<T>(): ReturnCreateContext<T> {
37
38
} ) ) ;
38
39
39
40
useLayoutEffect ( ( ) => {
40
- context . listeners . forEach ( listener => {
41
- listener ( value ) ;
41
+ unstable_batchedUpdates ( ( ) => {
42
+ context . listeners . forEach ( listener => {
43
+ listener ( value ) ;
44
+ } ) ;
42
45
} ) ;
43
46
} , [ value ] ) ;
44
47
@@ -48,23 +51,55 @@ export function createContext<T>(): ReturnCreateContext<T> {
48
51
return { Context, Provider } ;
49
52
}
50
53
51
- export function useContextSelector < T , O > ( holder : ReturnCreateContext < T > , selector : Selector < T , O > ) {
52
- const eventSelector = useEvent ( selector ) ;
54
+ export function useContextSelector < T , O > (
55
+ holder : ReturnCreateContext < T > ,
56
+ selector : Selector < T , O > ,
57
+ ) : O ;
58
+ export function useContextSelector < T , O extends Partial < T > > (
59
+ holder : ReturnCreateContext < T > ,
60
+ selector : ( keyof T ) [ ] ,
61
+ ) : O ;
62
+ export function useContextSelector < T , S extends keyof T > (
63
+ holder : ReturnCreateContext < T > ,
64
+ selector : S ,
65
+ ) : T [ S ] ;
66
+
67
+ export function useContextSelector < T , O > (
68
+ holder : ReturnCreateContext < T > ,
69
+ selector : Selector < T , any > | ( keyof T ) [ ] | keyof T ,
70
+ ) {
71
+ const eventSelector = useEvent < Selector < T , O > > (
72
+ typeof selector === 'function'
73
+ ? selector
74
+ : ctx => {
75
+ if ( ! Array . isArray ( selector ) ) {
76
+ return ctx [ selector ] ;
77
+ }
78
+
79
+ const obj = { } as O ;
80
+ selector . forEach ( key => {
81
+ ( obj as any ) [ key ] = ctx [ key ] ;
82
+ } ) ;
83
+ return obj ;
84
+ } ,
85
+ ) ;
53
86
const context = React . useContext ( holder ?. Context ) ;
54
87
const { listeners, getValue } = context || { } ;
55
88
56
- const [ value , setValue ] = React . useState ( ( ) => eventSelector ( context ? getValue ( ) : null ) ) ;
89
+ const valueRef = React . useRef < O > ( ) ;
90
+ valueRef . current = eventSelector ( context ? getValue ( ) : null ) ;
91
+ const [ , forceUpdate ] = React . useState ( { } ) ;
57
92
58
93
useLayoutEffect ( ( ) => {
59
94
if ( ! context ) {
60
95
return ;
61
96
}
62
97
63
98
function trigger ( nextValue : T ) {
64
- setValue ( prev => {
65
- const selectedValue = eventSelector ( nextValue ) ;
66
- return shallowEqual ( prev , selectedValue ) ? prev : selectedValue ;
67
- } ) ;
99
+ const nextSelectorValue = eventSelector ( nextValue ) ;
100
+ if ( ! shallowEqual ( valueRef . current , nextSelectorValue ) ) {
101
+ forceUpdate ( { } ) ;
102
+ }
68
103
}
69
104
70
105
listeners . add ( trigger ) ;
@@ -74,5 +109,5 @@ export function useContextSelector<T, O>(holder: ReturnCreateContext<T>, selecto
74
109
} ;
75
110
} , [ context ] ) ;
76
111
77
- return value ;
112
+ return valueRef . current ;
78
113
}
0 commit comments