Build a custom React hook that manages a list. Working with arrays in state is where a lot of React bugs are born: it is tempting to reach for push, splice, or sort, but those methods change the existing array in place — and React decides whether to re-render by checking whether the array is a new value, not by inspecting its contents. useArray(initial) owns the list and hands back the current array plus five helpers that change it the right way: set, push, remove, update, and clear.
function useArray<T>(initial?: T[]): {
array: T[];
set: (next: T[]) => void;
push: (item: T) => void;
remove: (index: number) => void;
update: (index: number, item: T) => void;
clear: () => void;
};
initial defaults to an empty array when omitted. set replaces the whole array; push appends one item; remove drops the element at an index; update replaces the element at an index; clear empties the list back to [].
function TagList() {
const { array, push, remove, clear } = useArray(['react', 'hooks']);
return (
<div>
{array.map((tag, i) => (
<button key={i} onClick={() => remove(i)}>{tag} x</button>
))}
<button onClick={() => push('state')}>add</button>
<button onClick={clear}>clear</button>
</div>
);
}
// renders react, hooks; add -> react, hooks, state; clicking "hooks x" -> react, state
// Each helper returns a brand-new array; the old one is never mutated.
const { array, push, update, remove } = useArray([10, 20, 30]);
push(40); // [10, 20, 30, 40]
update(0, 99); // [99, 20, 30, 40]
remove(1); // [99, 30, 40]
array.push(item) changes the existing array and keeps the same reference. React compares the new value to the old by reference, sees no change, and skips the re-render — so the screen goes stale.[...array, item]), filter, and map, which return fresh arrays and leave the original alone.remove(99) on a three-item list should leave the array unchanged, not throw.