Skip to content

Instantly share code, notes, and snippets.

@jasonm23
Last active November 15, 2025 10:38
Show Gist options
  • Select an option

  • Save jasonm23/e7ea9731675fd7a9941491ba595deb59 to your computer and use it in GitHub Desktop.

Select an option

Save jasonm23/e7ea9731675fd7a9941491ba595deb59 to your computer and use it in GitHub Desktop.
Not sure this already exists, does now: Finite/Wrap Around generic history React hook. - Note this is value history so an abstraction lower than things like browser history.
import { useState, useCallback } from 'react';
interface History<T> {
history: T[];
head: T | undefined;
previous: T | undefined;
next: T | undefined;
addItem: (item: T) => void;
previousItem: () => void;
nextItem: () => void;
removePrev: (n: number) => void;
removeAll: () => void;
}
interface UseHistoryOptions<T> {
maxLength: number;
init?: T[];
allowDuplicates?: boolean;
}
export function useHistory<T>({
maxLength,
init = [],
allowDuplicates = true
}: UseHistoryOptions<T>): History<T> {
const [history, setHistory] = useState<T[]>([...init]);
const [currentIndex, setCurrentIndex] = useState(init.length ? init.length - 1 : -1);
const addItem = useCallback(
(item: T) => {
setHistory((prev) => {
if (!allowDuplicates && prev[prev.length - 1] === item) return prev;
const next =
prev.length >= maxLength
? [...prev.slice(1), item]
: [...prev, item];
setCurrentIndex(next.length - 1);
return next;
});
},
[allowDuplicates, maxLength]
);
const previousItem = useCallback(() => {
setCurrentIndex((i) => (i > 0 ? i - 1 : i));
}, []);
const nextItem = useCallback(() => {
setCurrentIndex((i) => (i < history.length - 1 ? i + 1 : i));
}, [history.length]);
const removePrev = useCallback((n: number) => {
setHistory((prev) => {
const next = prev.slice(n);
setCurrentIndex(next.length - 1);
return next;
});
}, []);
const removeAll = useCallback(() => {
setHistory([]);
setCurrentIndex(-1);
}, []);
return {
history,
head: history[currentIndex],
previous: currentIndex > 0 ? history[currentIndex - 1] : undefined,
next: currentIndex >= 0 && currentIndex < history.length - 1 ? history[currentIndex + 1] : undefined,
addItem,
previousItem,
nextItem,
removePrev,
removeAll
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment