( initialState?: IHookStateInitAction<S>, capacity: number = 10, initialHistory?: I[] )
| 21 | export function useStateWithHistory<S = undefined>(): UseStateHistoryReturn<S | undefined>; |
| 22 | |
| 23 | export function useStateWithHistory<S, I extends S>( |
| 24 | initialState?: IHookStateInitAction<S>, |
| 25 | capacity: number = 10, |
| 26 | initialHistory?: I[] |
| 27 | ): UseStateHistoryReturn<S> { |
| 28 | if (capacity < 1) { |
| 29 | throw new Error(`Capacity has to be greater than 1, got '${capacity}'`); |
| 30 | } |
| 31 | |
| 32 | const isFirstMount = useFirstMountState(); |
| 33 | const [state, innerSetState] = useState<S>(initialState as S); |
| 34 | const history = useRef<S[]>((initialHistory ?? []) as S[]); |
| 35 | const historyPosition = useRef(0); |
| 36 | |
| 37 | // do the states manipulation only on first mount, no sense to load re-renders with useless calculations |
| 38 | if (isFirstMount) { |
| 39 | if (history.current.length) { |
| 40 | // if last element of history !== initial - push initial to history |
| 41 | if (history.current[history.current.length - 1] !== initialState) { |
| 42 | history.current.push(initialState as I); |
| 43 | } |
| 44 | |
| 45 | // if initial history bigger that capacity - crop the first elements out |
| 46 | if (history.current.length > capacity) { |
| 47 | history.current = history.current.slice(history.current.length - capacity); |
| 48 | } |
| 49 | } else { |
| 50 | // initiate the history with initial state |
| 51 | history.current.push(initialState as I); |
| 52 | } |
| 53 | |
| 54 | historyPosition.current = history.current.length && history.current.length - 1; |
| 55 | } |
| 56 | |
| 57 | const setState = useCallback( |
| 58 | (newState: IHookStateSetAction<S>): void => { |
| 59 | innerSetState((currentState) => { |
| 60 | newState = resolveHookState(newState, currentState); |
| 61 | |
| 62 | // is state has changed |
| 63 | if (newState !== currentState) { |
| 64 | // if current position is not the last - pop element to the right |
| 65 | if (historyPosition.current < history.current.length - 1) { |
| 66 | history.current = history.current.slice(0, historyPosition.current + 1); |
| 67 | } |
| 68 | |
| 69 | historyPosition.current = history.current.push(newState as I) - 1; |
| 70 | |
| 71 | // if capacity is reached - shift first elements |
| 72 | if (history.current.length > capacity) { |
| 73 | history.current = history.current.slice(history.current.length - capacity); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | return newState; |
| 78 | }); |
| 79 | }, |
| 80 | [state, capacity] |
searching dependent graphs…