react-use
Version:
Collection of React Hooks
90 lines (89 loc) • 4.14 kB
JavaScript
import { useCallback, useMemo, useRef, useState } from 'react';
import { useFirstMountState } from './useFirstMountState';
import { resolveHookState } from './misc/hookState';
export function useStateWithHistory(initialState, capacity, initialHistory) {
if (capacity === void 0) { capacity = 10; }
if (capacity < 1) {
throw new Error("Capacity has to be greater than 1, got '" + capacity + "'");
}
var isFirstMount = useFirstMountState();
var _a = useState(initialState), state = _a[0], innerSetState = _a[1];
var history = useRef((initialHistory !== null && initialHistory !== void 0 ? initialHistory : []));
var historyPosition = useRef(0);
// do the states manipulation only on first mount, no sense to load re-renders with useless calculations
if (isFirstMount) {
if (history.current.length) {
// if last element of history !== initial - push initial to history
if (history.current[history.current.length - 1] !== initialState) {
history.current.push(initialState);
}
// if initial history bigger that capacity - crop the first elements out
if (history.current.length > capacity) {
history.current = history.current.slice(history.current.length - capacity);
}
}
else {
// initiate the history with initial state
history.current.push(initialState);
}
historyPosition.current = history.current.length && history.current.length - 1;
}
var setState = useCallback(function (newState) {
innerSetState(function (currentState) {
newState = resolveHookState(newState, currentState);
// is state has changed
if (newState !== currentState) {
// if current position is not the last - pop element to the right
if (historyPosition.current < history.current.length - 1) {
history.current = history.current.slice(0, historyPosition.current + 1);
}
historyPosition.current = history.current.push(newState) - 1;
// if capacity is reached - shift first elements
if (history.current.length > capacity) {
history.current = history.current.slice(history.current.length - capacity);
}
}
return newState;
});
}, [state, capacity]);
var historyState = useMemo(function () { return ({
history: history.current,
position: historyPosition.current,
capacity: capacity,
back: function (amount) {
if (amount === void 0) { amount = 1; }
// don't do anything if we already at the left border
if (!historyPosition.current) {
return;
}
innerSetState(function () {
historyPosition.current -= Math.min(amount, historyPosition.current);
return history.current[historyPosition.current];
});
},
forward: function (amount) {
if (amount === void 0) { amount = 1; }
// don't do anything if we already at the right border
if (historyPosition.current === history.current.length - 1) {
return;
}
innerSetState(function () {
historyPosition.current = Math.min(historyPosition.current + amount, history.current.length - 1);
return history.current[historyPosition.current];
});
},
go: function (position) {
if (position === historyPosition.current) {
return;
}
innerSetState(function () {
historyPosition.current =
position < 0
? Math.max(history.current.length + position, 0)
: Math.min(history.current.length - 1, position);
return history.current[historyPosition.current];
});
},
}); }, [state]);
return [state, setState, historyState];
}