useEffect
Master useEffect — the hook for synchronising React components with the outside world. Learn the dependency array, cleanup functions, and how to avoid the most common production bugs.
Last reviewed: January 2025
🔍 What Is It?
useEffect(fn, deps) runs fn after the component renders. It is React's escape hatch for synchronising with external systems.
Three forms:
- →
useEffect(fn)— runs after every render - →
useEffect(fn, [])— runs once after the first render (mount) - →
useEffect(fn, [dep1, dep2])— runs after renders where dep1 or dep2 changed
Cleanup function:
Return a function from the effect to clean up before the next run or on unmount:
useEffect(() => {
const sub = subscribe(channel);
return () => sub.unsubscribe(); // cleanup
}, [channel]);🎯 Why Do We Need It?
React rendering must be a pure function — no side effects during render. useEffect is the designated place for side effects:
- →Data fetching — calling an API when a component mounts or a filter changes
- →Subscriptions — WebSocket connections, event listeners, Observables
- →Timers — setInterval, setTimeout
- →DOM manipulation — measuring elements, integrating third-party libraries
- →Synchronising external state — syncing React state to localStorage
Without useEffect, you would have no way to trigger these after rendering.
🏗️ What Happens in Real Projects?
In a real SaaS dashboard:
- →Fetch dashboard metrics when the selected date range changes
- →Subscribe to a WebSocket for real-time notifications
- →Sync filter state to URL query params
- →Set document.title to the current page name
- →Initialise a charting library (Chart.js, D3) on a canvas element
- →Clean up event listeners when a modal closes
⚙️ How It Works
Execution order:
- Component renders (JSX evaluated, DOM updated)
- Browser paints the screen
- useEffect runs
- On next render: cleanup of previous effect runs, then new effect runs
- On unmount: cleanup of last effect runs
Dependency array rules:
- →React uses Object.is() to compare dependencies
- →Objects and arrays are compared by reference, not value
- →The ESLint rule
react-hooks/exhaustive-depscatches missing dependencies — always follow it - →If a dependency changes every render (e.g., an inline object), the effect runs every render too
💻 Example
useEffect Patterns in Production — Data fetching, subscriptions, and cleanup patterns from real projects.
🤔 Common Confusions
❓ Why does my useEffect run twice in development?
React 18 Strict Mode intentionally mounts, unmounts, and remounts every component in development to help you find missing cleanup functions. This only happens in development — never in production. If your effect breaks on double-mount, your cleanup function is missing or incorrect.
❓ What happens if I omit a dependency from the array?
The effect captures a stale closure — it sees the value of the dependency from the render it was created in, not the current value. This causes subtle bugs where the effect uses outdated data. Always follow the exhaustive-deps ESLint rule. If adding a dependency causes an infinite loop, the real fix is restructuring the effect, not removing the dependency.
❓ Should I use useEffect for data fetching?
For simple cases, yes. For production apps, use React Query (TanStack Query) instead. It handles caching, background refetching, deduplication, loading/error states, and stale-while-revalidate automatically. Manual useEffect data fetching is error-prone and lacks these features.
🚨 Common Production Issues
**Memory leak: setState on unmounted component** — An async operation completes after the component unmounts and calls setState. Fix: use a cancellation flag in the cleanup function.
**Infinite loop: Object in dependency array** — useEffect(fn, [{ id: 1 }]) creates a new object every render, causing the effect to run every render. Fix: depend on primitives (id) not objects.
**Bug: Missing cleanup for subscriptions** — Event listeners, WebSockets, and intervals that are not cleaned up on unmount cause memory leaks and ghost callbacks. Always return a cleanup function.
**Bug: Race condition** — User changes a filter rapidly; multiple API calls are in flight; the last one to resolve wins, which may not be the most recent request. Fix: cancellation flag or AbortController.
✅ Best Practices
Always return a cleanup function for subscriptions, timers, and event listeners
Use a cancellation flag or AbortController for async operations
Follow the exhaustive-deps ESLint rule — never suppress it without understanding why
Depend on primitive values (id, string) not objects or arrays
Extract complex effects into custom hooks for reusability and testability
For data fetching in production, prefer React Query over manual useEffect
Never fetch data in useEffect with an empty dependency array in components that receive dynamic IDs via props
🏛️ Architect's Note
useEffect is the most misused hook in React. The most common mistake is treating it like componentDidMount — a place to run code once. React 18 and Strict Mode make it clear: every effect must be resilient to running twice and must clean up after itself. If you find yourself fighting useEffect, the answer is usually a custom hook or React Query, not a more clever effect. In large codebases, raw useEffect for data fetching is a code smell — it should be encapsulated in a custom hook or replaced with a data fetching library.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
React Technology Guide
Return to the React learning hub.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
Sources
Sources / References
Use these React references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide React learning path
Browse the full React topic collection and learning order on SplashRide.
React Interview Prep
Practice React interview questions and scenario-based answers.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
useState
Master useState — the most fundamental React hook. Learn how it works under the hood, the patterns senior engineers use, and the bugs that trip up even experienced developers.
useCallback
Master useCallback — the hook for stabilising function references between renders. Learn when it genuinely helps performance and when it is unnecessary overhead.