({
data,
save,
delay = 600,
enabled = true,
}: UseAutoSaveOptions<T>)
| 10 | } |
| 11 | |
| 12 | export function useAutoSave<T>({ |
| 13 | data, |
| 14 | save, |
| 15 | delay = 600, |
| 16 | enabled = true, |
| 17 | }: UseAutoSaveOptions<T>): { status: SaveStatus; flush: () => void; retry: () => void } { |
| 18 | const [status, setStatus] = useState<SaveStatus>('idle') |
| 19 | const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null) |
| 20 | const savedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null) |
| 21 | const latestDataRef = useRef<T>(data) |
| 22 | const saveRef = useRef(save) |
| 23 | const inflightRef = useRef(false) |
| 24 | const initialRef = useRef(true) |
| 25 | const pendingRef = useRef(false) |
| 26 | |
| 27 | latestDataRef.current = data |
| 28 | saveRef.current = save |
| 29 | |
| 30 | const doSave = useCallback(async () => { |
| 31 | if (inflightRef.current) { |
| 32 | pendingRef.current = true |
| 33 | return |
| 34 | } |
| 35 | inflightRef.current = true |
| 36 | setStatus('saving') |
| 37 | try { |
| 38 | await saveRef.current(latestDataRef.current) |
| 39 | setStatus('saved') |
| 40 | if (savedTimerRef.current) clearTimeout(savedTimerRef.current) |
| 41 | savedTimerRef.current = setTimeout(() => setStatus('idle'), 2000) |
| 42 | if (pendingRef.current) { |
| 43 | pendingRef.current = false |
| 44 | inflightRef.current = false |
| 45 | doSave() |
| 46 | return |
| 47 | } |
| 48 | } catch { |
| 49 | setStatus('error') |
| 50 | } finally { |
| 51 | inflightRef.current = false |
| 52 | } |
| 53 | }, []) |
| 54 | |
| 55 | useEffect(() => { |
| 56 | if (!enabled) return |
| 57 | if (initialRef.current) { |
| 58 | initialRef.current = false |
| 59 | return |
| 60 | } |
| 61 | if (timerRef.current) clearTimeout(timerRef.current) |
| 62 | timerRef.current = setTimeout(doSave, delay) |
| 63 | return () => { |
| 64 | if (timerRef.current) clearTimeout(timerRef.current) |
| 65 | } |
| 66 | }, [data, delay, enabled, doSave]) |
| 67 | |
| 68 | useEffect(() => { |
| 69 | return () => { |
no outgoing calls
no test coverage detected