MENU
UseIsMounted
In React 17 and earlier, calling a state setter after a component unmounted (for example, from a Promise that resolves after the user has navigated away) logged a console warning: Can't perform a React state update on an unmounted component. This useIsMounted hook guarded against that by tracking whether the component is still mounted before calling the setter.
React 18 removed that warning — calling a state setter after unmount is now a silent no-op, so this pattern is no longer needed just to silence it. The technique can still be useful if you specifically want to skip the async work's follow-up entirely once unmounted (not merely make the setter a no-op), but for most code you can rely on an AbortController or an effect cleanup flag instead.
RESETRUNFULL
<!DOCTYPE html><html><head>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head><body>
<div></div>
<script type="text/babel">
const { useRef, useEffect, useState } = React;
function useIsMounted() {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => isMounted.current = false;
}, []);
return isMounted;
}
function asyncOperation() {
return new Promise(resolve => setTimeout(() => resolve('data loaded'), 2000));
}
function Book() {
const isMounted = useIsMounted();
const [data, setData] = useState(null);
useEffect(() => {
asyncOperation().then(result => { if (isMounted.current) { setData(result); } });
}, []);
return <div>{data ? data : 'Loading...'}</div>;
}
function App() {
const [show, setShow] = useState(true);
return (
<React.Fragment>
<button onClick={() => setShow(s => !s)}>Toggle Book</button>
{show && <Book/>}
</React.Fragment>
);
}
const root = ReactDOM.createRoot(document.querySelector("div"));
root.render(<App/>);
</script>
</body></html>