useEffect lets your React component run side effects after rendering finishes—such as subscribing to browser resize events, syncing localStorage, starting intervals, or fetching data from an API. Think of useEffect as a bridge connecting React's pure render cycle to external systems outside React.
The second argument to useEffect(setup, deps) determines when your effect runs:
useEffect(fn)): Runs after every render (rarely needed and prone to loops).useEffect(fn, [])): Runs once when the component mounts, with cleanup on unmount.useEffect(fn, [userId])): Runs on mount and whenever userId changes.flowchart TD
Mount["Component Mounts"] --> Run["Run Effect Setup"]
Run --> DepChange{"Dependency Changed?"}
DepChange -- Yes --> Cleanup["Run Previous Cleanup"] --> Run
DepChange -- No --> Unmount["Component Unmounts"] --> FinalCleanup["Run Final Cleanup"]
Always return a cleanup function to clear timers, remove event listeners, or abort in-flight network calls. Using AbortController prevents race conditions when a user switches tabs quickly before an earlier API response arrives.
import { useEffect, useState } from 'react';
export function UserViewer({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function loadUser() {
try {
const res = await fetch(`/api/users/${userId}`, { signal: controller.signal });
const data = await res.json();
setUser(data);
} catch (err) {
if (err.name !== 'AbortError') console.error(err);
}
}
loadUser();
return () => controller.abort();
}, [userId]);
if (!user) return <p>Loading user...</p>;
return <h2>{user.name}</h2>;
}
useEffect MistakesuseEffect for Derived State: Do not use useEffect just to compute filteredItems from items and searchQuery. Calculate derived values inline during render.async Directly: Passing useEffect(async () => ...) returns a Promise instead of a cleanup function. Define an inner async function and call it inside the effect.useEffect run twice in development?React <StrictMode> intentionally mounts, cleans up, and re-runs effects once in development to expose missing cleanup logic.
Include every reactive value (props, state, or functions defined inside the component) read inside the effect.
useEffect for form submission requests?No. Trigger POST/PUT requests directly inside event handlers (onSubmit or onClick) rather than useEffect.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With