File tree Expand file tree Collapse file tree 3 files changed +54
-0
lines changed Expand file tree Collapse file tree 3 files changed +54
-0
lines changed Original file line number Diff line number Diff line change 6363 "husky" : " ^8.0.3" ,
6464 "lint-staged" : " ^15.1.0" ,
6565 "np" : " ^10.0.2" ,
66+ "prettier" : " ^3.3.2" ,
6667 "rc-test" : " ^7.0.14" ,
6768 "react" : " ^18.0.0" ,
6869 "react-dom" : " ^18.0.0" ,
Original file line number Diff line number Diff line change 1+ import * as React from 'react' ;
2+ import useEvent from './useEvent' ;
3+
4+ type Updater < T > = T | ( ( prevValue : T ) => T ) ;
5+
6+ export type SetState < T > = ( nextValue : Updater < T > ) => void ;
7+
8+ /**
9+ * Same as React.useState but will always get latest state.
10+ * This is useful when React merge multiple state updates into one.
11+ * e.g. onTransitionEnd trigger multiple event at once will be merged state update in React.
12+ */
13+ export default function useSyncState < T > (
14+ defaultValue ?: T ,
15+ ) : [ get : ( ) => T , set : SetState < T > ] {
16+ const [ , forceUpdate ] = React . useReducer ( x => x + 1 , 0 ) ;
17+
18+ const currentValueRef = React . useRef ( defaultValue ) ;
19+
20+ const getValue = useEvent ( ( ) => {
21+ return currentValueRef . current ;
22+ } ) ;
23+
24+ const setValue = useEvent ( ( updater : Updater < T > ) => {
25+ currentValueRef . current =
26+ typeof updater === 'function'
27+ ? ( updater as ( prevValue : T ) => T ) ( currentValueRef . current )
28+ : updater ;
29+
30+ forceUpdate ( ) ;
31+ } ) ;
32+
33+ return [ getValue , setValue ] ;
34+ }
Original file line number Diff line number Diff line change @@ -7,6 +7,7 @@ import useMemo from '../src/hooks/useMemo';
77import useMergedState from '../src/hooks/useMergedState' ;
88import useMobile from '../src/hooks/useMobile' ;
99import useState from '../src/hooks/useState' ;
10+ import useSyncState from '../src/hooks/useSyncState' ;
1011
1112global . disableUseId = false ;
1213
@@ -521,4 +522,22 @@ describe('hooks', () => {
521522 ) ;
522523 } ) ;
523524 } ) ;
525+
526+ describe ( 'useSyncState' , ( ) => {
527+ it ( 'batch use latest' , ( ) => {
528+ const Demo = ( ) => {
529+ const [ getCounter , setCounter ] = useSyncState ( 0 ) ;
530+
531+ React . useEffect ( ( ) => {
532+ setCounter ( getCounter ( ) + 1 ) ;
533+ setCounter ( getCounter ( ) + 1 ) ;
534+ } , [ getCounter , setCounter ] ) ;
535+
536+ return getCounter ( ) ;
537+ } ;
538+
539+ const { container } = render ( < Demo /> ) ;
540+ expect ( container . textContent ) . toEqual ( '2' ) ;
541+ } ) ;
542+ } ) ;
524543} ) ;
You can’t perform that action at this time.
0 commit comments