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:
useEffectis 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,
useEffectruns only when one of the dependencies has changed since the last render. - If an empty dependencies array (
[]) is provided,useEffectruns 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
useEffecthook 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,useEffectruns only once, after the initial render. - Any state changes (like clicking the button to increment the count) will not trigger
useEffectagain, 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.