10
10
* governing permissions and limitations under the License.
11
11
*/
12
12
13
- import { Dispatch , useCallback , useRef , useState } from 'react' ;
14
- import { useLayoutEffect } from './' ;
13
+ import { Dispatch , useRef , useState } from 'react' ;
14
+ import { useEffectEvent , useLayoutEffect } from './' ;
15
15
16
16
type SetValueAction < S > = ( prev : S ) => Generator < any , void , unknown > ;
17
17
@@ -21,48 +21,41 @@ type SetValueAction<S> = (prev: S) => Generator<any, void, unknown>;
21
21
// written linearly.
22
22
export function useValueEffect < S > ( defaultValue : S | ( ( ) => S ) ) : [ S , Dispatch < SetValueAction < S > > ] {
23
23
let [ value , setValue ] = useState ( defaultValue ) ;
24
- let valueRef = useRef ( value ) ;
25
24
let effect = useRef ( null ) ;
26
25
27
- // Must be stable so that `queue` is stable.
28
- let nextIter = useCallback ( ( ) => {
26
+ // Store the function in a ref so we can always access the current version
27
+ // which has the proper `value` in scope.
28
+ let nextRef = useEffectEvent ( ( ) => {
29
29
// Run the generator to the next yield.
30
30
let newValue = effect . current . next ( ) ;
31
- while ( ! newValue . done && valueRef . current === newValue . value ) {
32
- // If the value is the same as the current value,
33
- // then continue to the next yield. Otherwise,
34
- // set the value in state and wait for the next layout effect.
35
- newValue = effect . current . next ( ) ;
36
- }
31
+
37
32
// If the generator is done, reset the effect.
38
33
if ( newValue . done ) {
39
34
effect . current = null ;
40
35
return ;
41
36
}
42
37
43
- // Always update valueRef when setting the state.
44
- // This is needed because the function is not regenerated with the new state value since
45
- // they must be stable across renders. Instead, it gets carried in the ref, but the setState
46
- // is also needed in order to cause a rerender.
47
- setValue ( newValue . value ) ;
48
- valueRef . current = newValue . value ;
49
- // this list of dependencies is stable, setState and refs never change after first render.
50
- } , [ setValue , valueRef , effect ] ) ;
38
+ // If the value is the same as the current value,
39
+ // then continue to the next yield. Otherwise,
40
+ // set the value in state and wait for the next layout effect.
41
+ if ( value === newValue . value ) {
42
+ nextRef ( ) ;
43
+ } else {
44
+ setValue ( newValue . value ) ;
45
+ }
46
+ } ) ;
51
47
52
48
useLayoutEffect ( ( ) => {
53
49
// If there is an effect currently running, continue to the next yield.
54
50
if ( effect . current ) {
55
- nextIter ( ) ;
51
+ nextRef ( ) ;
56
52
}
57
53
} ) ;
58
54
59
- // queue must be a stable function, much like setState.
60
- let queue = useCallback ( fn => {
61
- effect . current = fn ( valueRef . current ) ;
62
- nextIter ( ) ;
63
- // this list of dependencies is stable, setState and refs never change after first render.
64
- // in addition, nextIter is stable as outlined above
65
- } , [ nextIter , effect , valueRef ] ) ;
55
+ let queue = useEffectEvent ( fn => {
56
+ effect . current = fn ( value ) ;
57
+ nextRef ( ) ;
58
+ } ) ;
66
59
67
60
return [ value , queue ] ;
68
61
}
0 commit comments