import { useState, useCallback } from 'react'; interface IData { present?: T; past: T[]; future: T[]; } const dumpIndex = (step: number, arr: T[]) => { let index = step > 0 ? step - 1 // move forward : arr.length + step; // move backward if (index >= arr.length - 1) { index = arr.length - 1; } if (index < 0) { index = 0; } return index; }; const split = (step: number, targetArr: T[]) => { const index = dumpIndex(step, targetArr); return { _current: targetArr[index], _before: targetArr.slice(0, index), _after: targetArr.slice(index + 1), }; }; export default function useHistoryTravel(initialValue?: T) { const [history, setHistory] = useState>({ present: initialValue, past: [], future: [] }); const { present, past, future } = history; const updateValue = useCallback((val: T) => { setHistory({ present: val, future: [], past: [...past, present] }); }, [history, setHistory]); const _forward = useCallback((step: number = 1) => { if (future.length === 0) return; const { _before, _current, _after } = split(step, future); setHistory({ present: _current, future: _after, past: [...past, present, ..._before] }); }, [history, setHistory]); const _backward = useCallback( (step: number = -1) => { if (past.length === 0) { return; } const { _before, _current, _after } = split(step, past); setHistory({ past: _before, present: _current, future: [..._after, present, ...future], }); }, [history, setHistory], ); const go = useCallback( (step: number) => { if (step === 0) { return; } if (step > 0) { return _forward(step); } _backward(step); }, [_backward, _forward], ); return { value: present, setValue: updateValue, backLength: past.length, forwardLength: future.length, go, back: useCallback(() => { go(-1); }, [go]), forward: useCallback(() => { go(1); }, [go]), }; }