Implement a useArray hook that manages an array of items with additional utility methods.
It is more convenient to use useArray over plain useState because in the latter case, you would always have to create a new array, mutate it, then set state to use the new array, which can be quite cumbersome.
The hook should work generically with arrays of any types.
const defaultValue = ['apple', 'banana'];export default function Component() {const { array, push, update, remove, filter, set, clear } =useArray(defaultValue);return (<div><p>Fruits: {array.join(', ')}</p><button onClick={() => push('orange')}>Add orange</button><button onClick={() => update(1, 'grape')}>Change second item to grape</button><button onClick={() => remove(0)}>Remove first</button><button onClick={() => filter((fruit) => fruit.includes('a'))}>Keep fruits containing 'a'</button><button onClick={() => set(defaultValue)}>Reset</button><button onClick={clear}>Clear list</button></div>);}
defaultValue: The initial array of itemsThe hook returns an object with the following properties:
array: The current array of itemsset: (newArray) => void: A function that sets the array of items. This must be the same type as the setter function of useStatepush: (item) => void: A function that adds an item to the end of the arrayremove: (index: number) => void: A function that removes an item from the array by indexfilter: (predicate) => void: A function that filters the array based on a predicate function. predicate must be the same type as the argument of Array.prototype.filterupdate: (index: number, newItem) => void: A function that replaces an item in the array at indexclear: () => void: A function that clears the arrayuseArray exposes one array state plus a small set of helper methods for replacing it immutably. The public contract is simple: array is the current value, set exposes React's raw setter, and the other methods are convenience operations built on top of that setter.
The main correctness concern is stale state. Helpers like push, filter, update, and remove should always work from the latest array value, even if several updates are queued together. That is why they all use the functional form of setArray(...) instead of reading array from the closure.
useState owns the array, React's built-in setter stays stable on its own, and useCallback keeps the custom helper functions stable for consumers that pass them to child components or effects.
import { useCallback, useState } from 'react';/*** @template T* @param {T[]} defaultValue*/export default function useArray(defaultValue) {const [array, setArray] = useState(defaultValue);const push = useCallback((element) => setArray((a) => [...a, element]), []);const filter = useCallback((callback) => setArray((a) => a.filter(callback)),[],);const update = useCallback((index, newElement) =>setArray((a) => [...a.slice(0, index),newElement,...a.slice(index + 1, a.length),]),[],);const remove = useCallback((index) =>setArray((a) => [...a.slice(0, index), ...a.slice(index + 1, a.length)]),[],);const clear = useCallback(() => setArray([]), []);return { array, set: setArray, push, filter, update, remove, clear };}
clear() resets to a fresh empty array rather than reusing or mutating the existing one.console.log() statements will appear here.