useCycleListLoading saved progress…

useCycleList

Lots of UI is a ring: a carousel that loops, a theme toggle that rotates light → dark → system → light, a "next tip" button that never runs out. useCycleList captures that pattern — hold an index into an array and move it forward or backward with wraparound, so stepping off the end lands back at the start and stepping before the start lands on the last item.

Implement useCycleList(items, startIndex = 0). It returns [activeItem, controls], where controls exposes the current index plus next(), prev(), and setIndex(i) — all of which keep the index inside the array's bounds by wrapping.

Signature

function useCycleList(items, startIndex = 0) {
  // returns [activeItem, { index, next, prev, setIndex }]
}

Examples

const [color, { next }] = useCycleList(['red', 'green', 'blue']);
// color === 'red'
next(); // 'green'
next(); // 'blue'
next(); // wraps -> 'red'
const [tab, { prev, setIndex }] = useCycleList(['a', 'b', 'c'], 0);
prev();        // wraps back -> 'c'
setIndex(4);   // 4 % 3 === 1 -> 'b'
setIndex(-1);  // -> last item 'c'

Notes

  • Wraparound both waysnext past the end returns to index 0; prev before 0 returns to the last index. Modular arithmetic, not clamping.
  • setIndex normalizes — any integer maps into [0, length) via wraparound, so negatives and overshoots are valid.
  • Stable controlsnext/prev/setIndex should keep the same identity across renders (wrap them in useCallback) so consumers can pass them to memoized children.
  • Don't worry about empty arrays — assume items has at least one element.
Loading editor…