Async work outlives components. A fetch kicked off in an effect can resolve after the user navigated away and the component unmounted — and calling setState at that point does nothing useful and historically warned about "state updates on an unmounted component." useIsMounted hands you a cheap guard: a function you call right before a late setState to check "am I still here?"
Implement useIsMounted(). It returns a stable function isMounted() that reports true while the component is mounted and false after it has unmounted. The function's identity must not change across renders, so it's safe to close over inside async callbacks.
function useIsMounted() {
// returns () => boolean — true while mounted, false after unmount
}
const isMounted = useIsMounted();
useEffect(() => {
fetchUser(id).then((user) => {
if (isMounted()) setUser(user); // skip if we've unmounted
});
}, [id]);
const isMounted = useIsMounted();
isMounted(); // true now
// ...component unmounts...
isMounted(); // false
true when the effect runs and false in its cleanup, which fires on unmount.useCallback (empty deps) so async closures capture one identity.isMounted() reads the current value; it doesn't re-render anything when the value changes.