I have this below function. My randomize function is the same across renders, as I have wrapped it in a useCallback. When I click the randomize button, it re-renders my app.
However, when I click that button, since randomize is memoized, don't I use the old setNum function? How does this work? Aren't the setter functions linked to their respective states, so the stale setter function would be changing an oudated state? Is it best practice to include the setter a dependency? And what practical difference does it make since the code seems to work as is?
export default function App() {
const [num, setNum] = useState(0);
const randomize = useCallback(() => {
setNum(Math.random());
}, []);
return (
<div className="App">
<h4>{num}</h4>
<button onClick={randomize}>Randomize</button>
</div>
);
}
There are no stateful values referenced inside the
useCallback, so there's no stale state that could cause issues.Additionally, state setters are stable references - it's the exact same function across all renders. (See below for an example.) Each different
setNumis not tied only to its own render - you can call any reference to it at any time, and the component will then re-render.In general, yes, it's a good idea to include as a dependency everything that's being referenced inside - but ESLint's rules of hooks is intelligent enough to recognize that the function returned by
useStateis stable, and thus doesn't need to be included in the dependency array. (Pretty much anything else from props or state should be included in the dependency array though, andexhaustive-depswill warn you when there's something missing)