UNPKG

2.08 kBTypeScriptView Raw
1import * as React from 'react';
2
3const UNINTIALIZED_STATE = {};
4
5/**
6 * This is definitely not compatible with concurrent mode, but we don't have a solution for sync state yet.
7 */
8export 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 // @ts-expect-error: initialState is a function, but TypeScript doesn't think so
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 // Make sure that the tracking state is up-to-date.
59 // We call it unconditionally, but React should skip the update if state is unchanged.
60 setTrackingState(stateRef.current);
61 }, []);
62
63 // If we're rendering and the tracking state is out of date, update it immediately
64 // This will make sure that our updates are applied as early as possible.
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}