Learn to master the useEffect dependency array to control exactly when your side effects run. Avoid infinite loops and optimize your React components today.
Previously in this course, we covered the Introduction to Side Effects: Managing External Logic in React, where we established that side effects—like fetching data or manually updating the DOM—should live outside the main render logic. Today, we add the "when" to the "what" by mastering the useEffect dependency array.
In React, the useEffect hook is your primary tool for syncing your component with the outside world. However, if you don't control when that sync happens, you'll quickly run into performance bottlenecks or, worse, infinite re-render loops.
The useEffect hook takes two arguments: a callback function (the effect) and an optional dependency array. The dependency array acts as a gatekeeper; it tells React, "Only run this effect if these specific values have changed since the last render."
If you omit the array entirely, the effect runs after every single render. This is usually not what you want.
To run an effect exactly once—when the component first appears on the screen—you provide an empty dependency array: [].
JAVASCRIPTimport { useEffect } from CE9178">'react'; function MovieLogger() { useEffect(() => { console.log("Component mounted! This runs only once."); }, []); // Empty array = mount only return <div>Check the console!</div>; }
If you want the effect to react to specific data, you list those variables inside the array. React compares the value from the previous render to the current render. If they differ, the effect fires.
In our movie-browser app, imagine we want to log the current search query every time the user types:
JAVASCRIPTimport { useState, useEffect } from CE9178">'react'; function SearchLogger({ query }) { useEffect(() => { console.log("Search query changed to:", query); }, [query]); // Runs whenever CE9178">'query' changes return <div>Searching for: {query}</div>; }
Let’s advance our project. We want the browser's tab title to update whenever the user changes their search query, so they always know what they are looking for.
JSXimport { useState, useEffect } from CE9178">'react'; export default function MovieBrowser() { const [query, setQuery] = useState(CE9178">''); // Sync document title with the search state useEffect(() => { document.title = query ? CE9178">`Searching for: ${query}` : CE9178">'Movie Browser'; }, [query]); // Dependency: re-run only when CE9178">'query' changes return ( <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search movies..." /> ); }
In this example, the effect is "reactive." It doesn't matter how many times the MovieBrowser component re-renders for other reasons; the document.title logic only executes if query has a new value.
useEffect that logs a message to the console saying "Effect triggered!"console.log("Rendering component...") at the top level of your component function.useEffect for data that can be calculated during render. If you're just transforming data (like sorting a list), do it directly in the component body instead of syncing it to state via an effect. Read more on why you should stop using useEffect for data calculations to keep your components fast.useEffect.[] ensures the effect runs only once on mount.Up next: We'll take these concepts further by learning how to fetch live movie data from an API and handle the asynchronous nature of network requests.
React useEffect is for synchronizing external systems, not state management. Learn why treating it as a lifecycle method leads to bugs and how to fix it.
useEffect Dependencies
Prop Drilling and Context API
Polishing the UI
Finalizing the Movie Browser
Review of Component Lifecycle
Review of State Management
Building a Modal Component
Introduction to PropTypes
Performance Optimization Basics
Handling Browser History
Working with LocalStorage
Building a Favorites List
Handling Media in React
Introduction to Testing
Debugging React Apps
Deployment Basics
Using External Libraries
Advanced