Mastering useEffect: A React Developer's Essential Hook
What is useEffect?
useEffect is a React hook that lets you perform side effects in functional components. Side effects can include things like:
Data fetching (e.g., from an API)
Subscribing to external services (e.g., WebSocket or events)
Manipulating the DOM directly
Starting/stopping timers
useEffect helps us manage these tasks, especially those that need to be done after a component has been rendered or in response to changes in state/props.
Basic Syntax of useEffect:
useEffect(() => {
// Your side effect code here (e.g., data fetch, subscriptions, etc.)
// Optional cleanup function to run when component unmounts or before re-running effect
return () => {
// Clean up side effects (e.g., clearInterval, unsubscribe, etc.)
};
}, [dependencies]); // Dependencies array
Callback Function: The first argument is the function (effect) you want to run. This is your "side effect" code.
Dependencies Array: The second argument is an array of dependencies. It tells React when to run (or re-run) the effect.
When Does
useEffectRun?The timing of when
useEffectruns depends on the dependencies array ([]) passed as the second argument:No Dependencies Array (
useEffect(() => {...})):useEffectwill run after every render (on every component update).This is generally inefficient unless you specifically need the effect to run on every render.
Example:
useEffect(() => { console.log('This runs after every render'); });
Empty Dependencies Array (
useEffect(() => {...}, [])):If you pass an empty array (
[]),useEffectwill run only once—after the initial render (when the component mounts).This mimics the behavior of
componentDidMountin class components.Example:
useEffect(() => { console.log('This runs only once, on mount'); }, []); // No dependencies, runs only once
With Dependencies (
useEffect(() => {...}, [dep1, dep2])):If you pass an array of dependencies,
useEffectwill run once initially (on mount) and whenever one or more dependencies change.Example:
useEffect(() => { console.log('This runs when "prop" or "state" changes'); }, [prop, state]); // Runs when "prop" or "state" changes
Cleanup Function:
Inside
useEffect, you can optionally return a cleanup function. This function will be called:When the component unmounts (to avoid memory leaks).
Just before the effect is re-run, in case dependencies change.
Example:
useEffect(() => { const timerId = setInterval(() => { console.log('Timer is running'); }, 1000); // Cleanup function to clear the interval return () => { clearInterval(timerId); // Runs when component unmounts }; }, []); // Runs only once
Common Use Cases for useEffect:
Fetching Data from APIs:
You often want to fetch data from a server after the component mounts. To do this, you would use
useEffectwith an empty dependencies array so it runs only once.Example:
useEffect(() => { async function fetchData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); setData(data); // Assume setData updates your state } fetchData(); // Call the async function }, []); // Run once after the initial render
Listening for Events (e.g., resizing window, mouse movement):
You might want to listen to events, like window resizing, and perform an action. Here, you also want to clean up the event listener when the component unmounts.
Example:
useEffect(() => { const handleResize = () => { console.log('Window resized'); }; window.addEventListener('resize', handleResize); // Cleanup the event listener when the component unmounts return () => { window.removeEventListener('resize', handleResize); }; }, []); // Add and remove event listener only once
Setting up and Cleaning up Timers:
You can use
useEffectto create and clean up timers. For example, starting a timer on mount and cleaning it up when the component unmounts.Example:
useEffect(() => { const timerId = setInterval(() => { console.log('Timer running'); }, 1000); // Clean up the timer on unmount return () => { clearInterval(timerId); }; }, []); // Only run once when component mounts
Re-running Effects When Dependencies Change:
Sometimes, you want to run a side effect only when certain state or props change. In this case, you pass those variables as dependencies in the array.
Example:
const [count, setCount] = useState(0); useEffect(() => { console.log('Count has changed:', count); }, [count]); // Effect runs only when "count" changes
Key Things to Remember:
Always provide a cleanup function if the effect sets up an ongoing process (like an interval, subscription, or event listener). Failure to clean up properly could lead to memory leaks or unwanted behavior.
Dependencies array:
If left out,
useEffectwill run after every render.If it's empty (
[]), it runs only once after the initial render (on mount).If it contains values, it will run whenever those values change.
Side effects and reactivity:
useEffectensures that side effects are kept in sync with the component’s render cycle, reducing errors and helping manage resources efficiently.