Managing an array in useState means retyping the same immutable spread gymnastics at every call site — setItems(items.filter((_, i) => i !== idx)) to remove, setItems([...items.slice(0, i), next, ...items.slice(i + 1)]) to update. It's easy to get an index wrong or accidentally mutate. useList packages the common array operations into a tidy, named API: push, removeAt, insertAt, updateAt, set, and clear, each doing the immutable update for you.
Implement useList(initialList = []). Return [list, actions], where actions holds those six helpers. Every helper produces a new array; nothing mutates state or the caller's original array.
function useList(initialList = []) {
// returns [list, { set, push, removeAt, insertAt, updateAt, clear }]
}
const [todos, { push, removeAt, updateAt }] = useList(['buy milk']);
push('walk dog'); // ['buy milk', 'walk dog']
updateAt(0, 'buy oat milk');
removeAt(1); // ['buy oat milk']
const [nums, { insertAt, clear }] = useList([1, 3]);
insertAt(1, 2); // [1, 2, 3]
clear(); // []
slice/filter/map; never push/splice state or the caller's array.insertAt(i, item) — the item ends up at index i, shifting the rest right; insertAt(0, x) prepends.removeAt / updateAt — target a single index without touching the others.actions object and its functions keep one identity across renders (use functional setState so they don't depend on list).