Build a custom React hook that behaves like useState but hands back one extra helper: a reset function that snaps the value back to whatever it started as. Forms, filter panels, and wizards all need a "start over" button, and re-implementing that by hand each time is busywork. useStateWithReset(initialValue) bundles the state, its setter, and a reset into a single tuple a component can drop in with one line.
function useStateWithReset<T>(
initialValue: T
): [
value: T,
setValue: (next: T | ((prev: T) => T)) => void,
reset: () => void,
];
value and setValue behave exactly like the pair from useState. reset() restores value to the original initialValue the hook was first created with.
function NameField() {
const [name, setName, reset] = useStateWithReset('');
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button onClick={reset}>Clear</button>
</div>
);
}
// typing fills the input; Clear snaps it back to ''
// setValue works with a direct value AND a functional updater, just like useState.
const [n, setN, reset] = useStateWithReset(0);
setN(5); // n === 5
setN((c) => c + 1); // n === 6
reset(); // n === 0 (back to the original)
reset targets the first initialValue. If the component re-renders and calls the hook with a different initialValue argument, reset must still return to the value the hook was created with on its very first render, not the new argument.setValue supports both forms. It must accept a direct value (setValue('x')) and a functional updater (setValue((prev) => ...)), exactly like useState's setter.[value, setValue, reset] in that order so callers can destructure and rename freely.reset should have a stable identity. Its reference should not change between renders, so it is safe to pass to memoized children or effect dependency arrays.