When using React hooks that interact with external systems (browser events, subscriptions, async requests), write code so it cannot leak resources or update state after the component unmounts.
Standard
useEffect, always return a cleanup function that removes listeners/subscriptions.useEffect (fetch, timers, requests), cancel/abort on cleanup (e.g., AbortController) so setState can’t run after unmount.useSyncExternalStore over manual event subscription logic.Example (safe effect + abort)
function Notifications() {
const [data, setData] = useState<Notification[]>([])
useEffect(() => {
const controller = new AbortController()
const onResize = () => {
// listener logic
}
window.addEventListener('resize', onResize)
fetch('/api/notifications', { signal: controller.signal })
.then(r => r.json())
.then(next => setData(next))
.catch(() => {
/* ignore abort */
})
return () => {
window.removeEventListener('resize', onResize)
controller.abort()
}
}, [])
return <div />
}
Example (subscription preference)
resize) via listeners, consider modeling that external value with useSyncExternalStore rather than ad-hoc addEventListener + setState patterns.Enter the URL of a public GitHub repository