useEffect is how you synchronize a React component with something outside of React: a browser API, a third-party library, a network connection. It is also the most misused hook in the ecosystem. Most of the useEffect code in production apps should not be useEffect at all -- it should be an event handler, a useMemo call, or server-side logic that never reaches the client.
Master react useeffect hook
Take the React Basics course with hands-on lessons and challenges.
AbortController cancels the in-flight request when userId changes or the component unmounts. Without this, a slow response for the old userId could overwrite the new user's data -- a classic race condition that only shows up when the network is slow.
When the user switches from AAPL to GOOGL, the cleanup closes the old WebSocket before opening a new one. This prevents receiving stale price updates for the wrong symbol. The status state gives the UI a way to show connection state without a separate loading boolean.
IntersectionObserver is a browser API -- exactly the kind of external system useEffect is designed for. The observer is disconnected on cleanup to prevent callbacks firing after unmount. Once the section is visible, we unobserve immediately since there is no reason to keep watching.
The cleanup restores the previous title when the component unmounts or when the title value changes. Extracting this into a custom hook keeps the component clean and makes the pattern reusable across pages. This is a textbook useEffect use case: syncing React state with a browser API that React does not control.
Global keyboard listeners must attach to the document and clean up on unmount. The callback is stabilized with useCallback to avoid removing and re-adding the listener on every render. This pattern is how command palettes (Cmd+K) and keyboard shortcuts work in tools like Linear, Notion, and VS Code.
The first anti-pattern filters a list inside useEffect, which causes two renders (one with stale data, one with filtered data). useMemo computes the filtered list during the same render with zero extra cycles. The second anti-pattern resets state via useEffect when a prop changes. A key prop tells React to unmount and remount the component, which resets all state cleanly without any effect.
Using useEffect to derive state from props or other state -- e.g. `useEffect(() => setFullName(first + ' ' + last), [first, last])`
Compute derived values during render: `const fullName = first + ' ' + last`. If the computation is expensive, wrap it in useMemo. useEffect causes a wasted render cycle because it runs after the component has already painted with stale data.
Missing the dependency array entirely, causing the effect to fire after every single render and often creating infinite update loops
Always provide a dependency array. Use `[]` for mount-only effects and `[dep1, dep2]` when the effect depends on reactive values. An effect with no array is almost always a bug.
Putting objects or functions created during render into the dependency array, which triggers the effect on every render because references change
Move object or function creation inside the effect body, or stabilize them with useMemo/useCallback. If the effect only uses specific properties of an object, destructure them and depend on the primitives instead.
Ignoring the exhaustive-deps ESLint rule by adding eslint-disable comments instead of fixing the dependency list
The rule exists because React cannot track what your effect reads. If the rule flags a missing dependency, either add it, move the value inside the effect, or restructure the code so the dependency is not needed. Suppressing the warning leads to stale closures that silently serve wrong data.
Fetching data in useEffect without cancellation, causing race conditions when dependencies change rapidly (e.g., user typing in a search box)
Always use AbortController for fetch requests. When the dependency changes, the cleanup function aborts the previous request so only the latest response is applied. Without this, a slow response for 'rea' can overwrite the results for 'react'.
useEffect exists for one purpose: synchronizing your component with something outside of React. Browser APIs, WebSocket connections, event listeners, third-party libraries -- these are legitimate use cases. If you are computing derived data, resetting state on prop change, or handling user actions, there is a better tool (useMemo, key props, event handlers). Always provide a dependency array, always clean up resources, and never suppress the exhaustive-deps rule. The best useEffect is the one you did not write.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.