The Effect Hook lets you perform side effects in function components.
What does useEffect
do?
By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.
Example Using UseEffect :
So here There is a function component 'Timer', and it has a 'count' variable that we can change it using the 'setCount' function.
There is a setTimeOut function that holds a function and executes it after the selected time (1000 milliseconds = 1 second here). After rendering the component, setTimeOut function will wait 1 second and then executes the 'setCount' function that which will add 1 to the 'count' variable.
Here we used useEffect without adding anything so it will recall the function inside it after any changes happened to the variable, And because 0 became 1, So here variable value has changed and useEffect will work for any change to the data variable then setTimeOut function will render again and will stay rendering, This means useEffect
runs on every render and thus the event handlers will unnecessarily get detached and reattached on each render.
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
});
return <h1>I have rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
result:
So Now How can we Solve this problem?
To solve it, In useEffect function, we can pass the second argument that it's an array, and this array can hold variables used in Dom as (dependencies).
So what's the difference between letting the Array empty or adding dependencies to the array?
Empty Array means that function inside useEffect will be rendered one time after the rendering of the component.
Even if any variable has changed it won't rerender.
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
}, []); // <- add empty brackets here
return <h1>I've rendered {count} times!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
result:
If Array has dependencies so the function inside useEffect it will render only if the specific variable inside the array has changed. So here if the 'count' variable has changed, the 'calculation' variable will also change.
import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";
function Counter() {
const [count, setCount] = useState(0);
const [calculation, setCalculation] = useState(0);
useEffect(() => {
setCalculation(() => count * 2);
}, [count]); // <- add the count variable here
return (
<>
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+</button>
<p>Calculation: {calculation}</p>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);
result:
Cleaning up useEffect
Finally, useEffect comes with a cleanup function, which you might not always need, but it can come in handy. the useEffect cleanup is a function in the useEffect Hook that allows us to tidy up our code before our component unmounts.
To invoke the cleanup function you can simply add a return function like so:
useEffect(() => {
// Your effect
return () => {
// Cleanup
};
}, []);
The cleanup can prevent memory leaks and remove unwanted things. Some use-cases for this are:
Clean up subscriptions
Clean up modals
Remove event listeners
Clear timeouts
Let's create an example where we have a function that adds something only after a specific time.
const [show, setShow] = useState(false);
useEffect(() => {
let timer = setTimeout(() => setShow(true), 3000);
}, []);
However, this will create a timeout in memory, so it would be best to clean this up.
For this let's add the cleanup function:
useEffect(() => {
let timer = setTimeout(() => setShow(true), 3000);
return () => {
clearTimeout(timer);
};
}, []);