11import * as React from 'react' ;
22import useEvent from './useEvent' ;
3- import useLayoutEffect , { useLayoutUpdateEffect } from './useLayoutEffect' ;
3+ import { useLayoutUpdateEffect } from './useLayoutEffect' ;
44import useState from './useState' ;
55
66type Updater < T > = (
77 updater : T | ( ( origin : T ) => T ) ,
88 ignoreDestroy ?: boolean ,
99) => void ;
1010
11- enum Source {
12- INNER ,
13- PROP ,
14- }
15-
16- type ValueRecord < T > = [ T , Source , T ] ;
17-
1811/** We only think `undefined` is empty */
1912function hasValue ( value : any ) {
2013 return value !== undefined ;
@@ -36,74 +29,40 @@ export default function useMergedState<T, R = T>(
3629 const { defaultValue, value, onChange, postState } = option || { } ;
3730
3831 // ======================= Init =======================
39- const [ mergedValue , setMergedValue ] = useState < ValueRecord < T > > ( ( ) => {
40- let finalValue : T = undefined ;
41- let source : Source ;
42-
32+ const [ innerValue , setInnerValue ] = useState < T > ( ( ) => {
4333 if ( hasValue ( value ) ) {
44- finalValue = value ;
45- source = Source . PROP ;
34+ return value ;
4635 } else if ( hasValue ( defaultValue ) ) {
47- finalValue =
48- typeof defaultValue === 'function'
49- ? ( defaultValue as any ) ( )
50- : defaultValue ;
51- source = Source . PROP ;
36+ return typeof defaultValue === 'function'
37+ ? ( defaultValue as any ) ( )
38+ : defaultValue ;
5239 } else {
53- finalValue =
54- typeof defaultStateValue === 'function'
55- ? ( defaultStateValue as any ) ( )
56- : defaultStateValue ;
57- source = Source . INNER ;
40+ return typeof defaultStateValue === 'function'
41+ ? ( defaultStateValue as any ) ( )
42+ : defaultStateValue ;
5843 }
59-
60- return [ finalValue , source , finalValue ] ;
6144 } ) ;
6245
63- const chosenValue = hasValue ( value ) ? value : mergedValue [ 0 ] ;
64- const postMergedValue = postState ? postState ( chosenValue ) : chosenValue ;
46+ const mergedValue = value !== undefined ? value : innerValue ;
47+ const postMergedValue = postState ? postState ( mergedValue ) : mergedValue ;
48+
49+ // ====================== Change ======================
50+ const onChangeFn = useEvent ( onChange ) ;
51+
52+ const [ prevValue , setPrevValue ] = React . useState < [ T ] > ( [ mergedValue ] ) ;
6553
66- // ======================= Sync =======================
6754 useLayoutUpdateEffect ( ( ) => {
68- setMergedValue ( ( [ prevValue ] ) => [ value , Source . PROP , prevValue ] ) ;
69- } , [ value ] ) ;
55+ const prev = prevValue [ 0 ] ;
56+ if ( innerValue !== prev ) {
57+ onChangeFn ( innerValue , prev ) ;
58+ }
59+ } , [ prevValue ] ) ;
7060
7161 // ====================== Update ======================
72- const changeEventPrevRef = React . useRef < T > ( ) ;
73-
7462 const triggerChange : Updater < T > = useEvent ( ( updater , ignoreDestroy ) => {
75- setMergedValue ( prev => {
76- const [ prevValue , prevSource , prevPrevValue ] = prev ;
77-
78- const nextValue : T =
79- typeof updater === 'function' ? ( updater as any ) ( prevValue ) : updater ;
80-
81- // Do nothing if value not change
82- if ( nextValue === prevValue ) {
83- return prev ;
84- }
85-
86- // Use prev prev value if is in a batch update to avoid missing data
87- const overridePrevValue =
88- prevSource === Source . INNER &&
89- changeEventPrevRef . current !== prevPrevValue
90- ? prevPrevValue
91- : prevValue ;
92-
93- return [ nextValue , Source . INNER , overridePrevValue ] ;
94- } , ignoreDestroy ) ;
63+ setInnerValue ( updater , ignoreDestroy ) ;
64+ setPrevValue ( [ mergedValue ] ) ;
9565 } ) ;
9666
97- // ====================== Change ======================
98- const onChangeFn = useEvent ( onChange ) ;
99-
100- useLayoutEffect ( ( ) => {
101- const [ current , source , prev ] = mergedValue ;
102- if ( current !== prev && source === Source . INNER ) {
103- onChangeFn ( current , prev ) ;
104- changeEventPrevRef . current = prev ;
105- }
106- } , [ mergedValue ] ) ;
107-
10867 return [ postMergedValue as unknown as R , triggerChange ] ;
10968}
0 commit comments