Introduction
React's built-in hook useEffect is used to manage side effects in a component, We use useEffect when we want a specific piece of code to execute as a result of a change in some dependency (change in variable or function).
Render Logic- The part of the component which gets executed as soon as a component is rendered and is responsible for UI rendering.
Side Effects- The part of the component which has interaction outside the component, such as HTTP request, DOM manipulation or setting up timers.
Side effects are commonly managed and controlled within React components using event handlers and the useEffect
hook.
What if Side Effects are in Render Logic
Including side effects in render logic can lead to performance issues and unexpected behavior, that's why side effects are isolated from render logic
Suppose we fetch some data in the render logic
function Component(){
const [fetchData,setFetchData]=useState([]);
//Fetching
fetch('https://api.example.com/data')
.then((res)=>res.json())
.then((data)=>setFetchData(data));
return <div>{fetchData}</div>
}
This will lead to an infinite loop of GET requests, when the component is mounted, it initiates data fetch and updates the state, As a result, the component is re-rendered, causing data fetch and update. Therefore this leads to infinite re-render and requests.
useEffect Syntax
The useEffect hook requires 2 arguments, the first argument is a function that contains the side-effect code and the second argument is a dependency array, it contains the dependency which when changed triggers the effect to run.
import {useEffect} from 'react'
function Component(){
useEffect(function(){
// side-effects
},[]); // []-> dependency Array
return <div>useEffect is awesome</div>
}
Dependency Array
Dependency arrays act as a method to determine when an effect should be executed, this array is passed in useEffect hook as an argument to prevent it from running after each render, after each render react compares the current values of the dependencies with their previous values, if there is change the effect is executed again.
So basically all the states and props used in the Effect function should be added to the dependency array so that components are synchronized to the side-effects.
There are total 3 conditions
No dependency Array
useEffect(function(){ // side-effect });
In this case, the effect is executed each time the component is rendered, as the dependency array is absent.
Empty Dependency Array
useEffect(function(){ // side-effect },[]);
In this case, there are no dependencies so the effect is only executed once after the component is mounted
Dependency Array
const [count1,setCount1]=useState(0); const [count2,setCount2]=useState(0); const [count3,setCount3]=useState(0); useEffect(function(){ document.title=count1+count2+count3; },[count1,count2,count3]);
In this case, we have count1, count2 & count3 as dependencies, Firstly the effect is executed after mount secondly each time any one or more of the dependencies is updated the effect is executed and the title of the page is updated
Chronology
At first, while the component is mounted the effect is not executed immediately, it is executed only after a component is rendered in UI, otherwise before rendering the UI the side effect could have blocked the code
useEffect(function(){
console.log("First");
},[]);
console.log("Second");
useEffect(function(){
console.log("Third")
});
On initial render we will be surprised to see, the order of the consoled value is different
//Initial Render
Second
First
Third
//Re-render
Second
Third
Here Second is part of the render logic and it is consoled during mounting and the effects are run after mounting that's why it is in this order.
Cleanup Function
Effects provides a method to clean up the effects which are no longer needed, we can provide a cleanup function by just returning a function from an effect. This is optional for some cases but is important in many, where it may lead to memory leaks.
This cleanup is executed in 2 cases-
Before the Effect is executed again due to a change in the dependency array, to reset the result of the previous side effect.
After the component is unmounted, to reset the result of the side effect.
This cleanup step ensures that any resources, event listeners, or subscriptions created by the effect are properly removed.
HTTP Req must have a cleanup to cancel the request and an added Event Listener must have a cleanup to remove the event listener.
For example, here we change the title of a react app based on a count state variable -
function Title() {
const [count, setCount] = useState(0);
useEffect(function {
document.title = `Count: ${count}`;
return function{
document.title = 'React App';
};
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
When the component unmounts or before the effect runs again, the cleanup function will reset the document title to 'React App'
Best Practices
Each effect must have a specific responsibility, instead of having all side effects in one effect, create individual effects.
All the state and prop used in the effect must be in the dependency array, otherwise react will have no idea when to execute the effects
Always provide a cleanup function to reset the results of the side effects.
useEffect must be at the top level of the component, not after early return or inside conditions
Conclusion
In conclusion, we can say useEffect ensures things happen at the right times, like fetching data when needed, updating the screen, or cleaning up after itself.
I hope this blog, gives a basic idea of Effects
Till Next Time!
Connect With Me ๐
If you're interested in topics like React.js, JavaScript or Web Development in General, Let's Connect!
LinkedIn: Click Here
Twitter: Click Here