import * as React from 'react'; const UNINTIALIZED_STATE = {}; /** * This is definitely not compatible with concurrent mode, but we don't have a solution for sync state yet. */ export default function useSyncState<T>(initialState?: (() => T) | T) { const stateRef = React.useRef<T>(UNINTIALIZED_STATE as any); const isSchedulingRef = React.useRef(false); const isMountedRef = React.useRef(true); React.useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); if (stateRef.current === UNINTIALIZED_STATE) { stateRef.current = // @ts-expect-error: initialState is a function, but TypeScript doesn't think so typeof initialState === 'function' ? initialState() : initialState; } const [trackingState, setTrackingState] = React.useState(stateRef.current); const getState = React.useCallback(() => stateRef.current, []); const setState = React.useCallback((state: T) => { if (state === stateRef.current || !isMountedRef.current) { return; } stateRef.current = state; if (!isSchedulingRef.current) { setTrackingState(state); } }, []); const scheduleUpdate = React.useCallback((callback: () => void) => { isSchedulingRef.current = true; try { callback(); } finally { isSchedulingRef.current = false; } }, []); const flushUpdates = React.useCallback(() => { if (!isMountedRef.current) { return; } // Make sure that the tracking state is up-to-date. // We call it unconditionally, but React should skip the update if state is unchanged. setTrackingState(stateRef.current); }, []); // If we're rendering and the tracking state is out of date, update it immediately // This will make sure that our updates are applied as early as possible. if (trackingState !== stateRef.current) { setTrackingState(stateRef.current); } const state = stateRef.current; React.useDebugValue(state); return [state, getState, setState, scheduleUpdate, flushUpdates] as const; }