import { useCallback, useRef, useState, Dispatch, SetStateAction } from "react"; type HistoryAction = { history: T[]; pointer: number; back: () => void; forward: () => void; go: (index: number) => void; }; type StateWithHistoryReturn = [ T, Dispatch>, HistoryAction ]; function useStateWithHistory( defaultValue: T, capacity: number = 10 ): StateWithHistoryReturn { const [value, setValue] = useState(defaultValue); const historyRef = useRef([value]); const pointerRef = useRef(0); const set = useCallback( (v: SetStateAction) => { const resolvedValue = typeof v === "function" ? (v as (prevState: T) => T)(value) : v; if (historyRef.current[pointerRef.current] !== resolvedValue) { if (pointerRef.current < historyRef.current.length - 1) { historyRef.current.splice(pointerRef.current + 1); } historyRef.current.push(resolvedValue); while (historyRef.current.length > capacity) { historyRef.current.shift(); } pointerRef.current = historyRef.current.length - 1; } setValue(resolvedValue); }, [capacity, value] ); const back = useCallback(() => { if (pointerRef.current <= 0) return; pointerRef.current--; setValue(historyRef.current[pointerRef.current]); }, []); const forward = useCallback(() => { if (pointerRef.current >= historyRef.current.length - 1) return; pointerRef.current++; setValue(historyRef.current[pointerRef.current]); }, []); const go = useCallback((index: number) => { if (index < 0 || index > historyRef.current.length - 1) return; pointerRef.current = index; setValue(historyRef.current[pointerRef.current]); }, []); const historyAction: HistoryAction = { history: historyRef.current, pointer: pointerRef.current, back, forward, go, }; return [value, set, historyAction]; } export default useStateWithHistory;