1 | import * as React from 'react';
|
2 |
|
3 | const UNINTIALIZED_STATE = {};
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | export default function useSyncState<T>(initialState?: (() => T) | T) {
|
9 | const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any);
|
10 | const isSchedulingRef = React.useRef(false);
|
11 | const isMountedRef = React.useRef(true);
|
12 |
|
13 | React.useEffect(() => {
|
14 | isMountedRef.current = true;
|
15 |
|
16 | return () => {
|
17 | isMountedRef.current = false;
|
18 | };
|
19 | }, []);
|
20 |
|
21 | if (stateRef.current === UNINTIALIZED_STATE) {
|
22 | stateRef.current =
|
23 |
|
24 | typeof initialState === 'function' ? initialState() : initialState;
|
25 | }
|
26 |
|
27 | const [trackingState, setTrackingState] = React.useState(stateRef.current);
|
28 |
|
29 | const getState = React.useCallback(() => stateRef.current, []);
|
30 |
|
31 | const setState = React.useCallback((state: T) => {
|
32 | if (state === stateRef.current || !isMountedRef.current) {
|
33 | return;
|
34 | }
|
35 |
|
36 | stateRef.current = state;
|
37 |
|
38 | if (!isSchedulingRef.current) {
|
39 | setTrackingState(state);
|
40 | }
|
41 | }, []);
|
42 |
|
43 | const scheduleUpdate = React.useCallback((callback: () => void) => {
|
44 | isSchedulingRef.current = true;
|
45 |
|
46 | try {
|
47 | callback();
|
48 | } finally {
|
49 | isSchedulingRef.current = false;
|
50 | }
|
51 | }, []);
|
52 |
|
53 | const flushUpdates = React.useCallback(() => {
|
54 | if (!isMountedRef.current) {
|
55 | return;
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 | setTrackingState(stateRef.current);
|
61 | }, []);
|
62 |
|
63 |
|
64 |
|
65 | if (trackingState !== stateRef.current) {
|
66 | setTrackingState(stateRef.current);
|
67 | }
|
68 |
|
69 | const state = stateRef.current;
|
70 |
|
71 | React.useDebugValue(state);
|
72 |
|
73 | return [state, getState, setState, scheduleUpdate, flushUpdates] as const;
|
74 | }
|