Build a custom React hook that steps through a fixed list of values, one at a time. useCycle('red', 'green', 'blue') hands back the current value (starting with the first) and a cycle function. Each call to cycle() moves to the next value, looping back to the start after the last — like clicking a toggle that rotates through a set of options. You can also pass an index to jump straight to a specific value. It's modeled on framer-motion's hook of the same name.
function useCycle<T>(...items: T[]): [current: T, cycle: (index?: number) => void];
current starts as items[0]. cycle() advances to the next item; cycle(index) jumps directly to items[index]. The cycle function keeps the same identity across every render.
function Theme() {
const [color, cycle] = useCycle('red', 'green', 'blue');
return <button onClick={() => cycle()} style={{ color }}>{color}</button>;
}
// renders 'red'; click → 'green'; click → 'blue'; click → wraps to 'red'
// Advancing wraps around; passing an index jumps directly.
const [value, cycle] = useCycle('a', 'b', 'c'); // value === 'a'
cycle(); // value === 'b'
cycle(); // value === 'c'
cycle(); // value === 'a' (wrapped past the end)
cycle(2); // value === 'c' (jumped to items[2])
cycle() call, current is items[0].cycle() on the last item returns to the first; the list is a loop, not a dead end.cycle accepts an optional index. cycle() steps forward by one; cycle(2) jumps straight to items[2].cycle must have a stable identity. Returning a brand-new function each render is not acceptable here — the same reference must come back across renders, so it's safe to pass to memoized children or effect dependency arrays.