does useEffect get called on every render?
The useEffect
hook in React is designed to run side effects in function components. By default, useEffect
runs after the first render and after every update. The reason it runs initially, even though you haven't clicked the button or changed the count, is because that's how useEffect
is designed to work.
Here's a detailed explanation:
How useEffect
Works
- Initial Render:
useEffect
is called after the initial render. This ensures that side effects (like fetching data, subscribing to services, etc.) are run as soon as the component mounts.
- Subsequent Renders:
- If the dependencies array is provided,
useEffect
runs only when one of the dependencies has changed since the last render. - If an empty dependencies array (
[]
) is provided,useEffect
runs only once, after the initial render.
- If the dependencies array is provided,
Example Breakdown
Let’s modify the previous example to include a useEffect
hook:
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect called');
}, []); // The empty array ensures this runs only once, after the initial render
console.log('Rendering App');
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Explanation
-
Initial Render:
console.log('Rendering App')
is executed when the component renders.- The
useEffect
hook runs after the initial render, logginguseEffect called
. - The component renders the UI, displaying the count and the button.
-
Effect Behavior:
- Because the dependencies array (
[]
) is empty,useEffect
runs only once, after the initial render. - Any state changes (like clicking the button to increment the count) will not trigger
useEffect
again, because the dependencies array is empty and doesn't includecount
.
- Because the dependencies array (
Common Patterns with useEffect
-
Run on Every Render:
useEffect(() => { console.log('useEffect called'); });
This runs after every render (initial and updates).
-
Run Once After Initial Render:
useEffect(() => { console.log('useEffect called'); }, []); // Empty array ensures this runs only once
-
Run When Dependencies Change:
useEffect(() => { console.log('useEffect called because count changed'); }, [count]); // Runs only when `count` changes
Practical Example with Side Effect
Here's a practical example of using useEffect
to fetch data when the component mounts:
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
useEffect(() => {
console.log('Fetching data...');
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // Fetch data only once when component mounts
console.log('Rendering App');
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<div>{data ? JSON.stringify(data) : 'Loading data...'}</div>
</div>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
In this example, useEffect
is used to fetch data from an API when the component mounts. The fetch
call runs only once due to the empty dependencies array.