Introduction
So, when I was a kid, my mama told me
"The new React hooks API is really cool, the useEffect
hook replaced componentDidMount
& componentDidUpdate
of the Class API"
I wanted to ask her more about it but she got busy optimizing her React component that was re-rendering multiple times.
When I got older, I learned more about React & these hooks API, and today I'll try explaining to YOU what my mom didn't explain to ME & Probably your mom didn't too β useEffect
Setup
So, here I created a React app in codesandbox on my browser
Yup! My Web browser!
Here's the link, interact with it yourself!
For those of you lazy folks who don't wanna go anywhere, here's the code π«
// App.js
import { useEffect, useState } from "react";
export default function App() {
const [counter, setCounter] = useState(0);
useEffect(() => {
console.log(counter);
});
return (
<div style={{ width: "100%", textAlign: "center" }}>
<h3>{counter}</h3>
<button onClick={() => setCounter((prevValue) => prevValue + 1)}>
Increment
</button>
</div>
);
}
Simple? huh...
Only 1 state variable defined counter
which is being incremented at every button tap by setCounter
, also a useEffect
with console.log(counter)
in its function body, nothing kubernetes!
Code
Let's now focus on the useEffect
defined here
useEffect(() => {
console.log(counter);
});
As evident, by the code, the hook takes a function which it'll internally call at "times". Those "times" can be the component re-render, initial component mount or any state update call which causes the component to, of course, re-render.
After running this code, I tap on the increment button 3 times & these are the console logs I get:
π€ Hmm... looks like, this particular useEffect
ran on these occasions:
- When the component is first mounted ( printed "0" to the console )
- When I tap the increment button or in short, the state update occurred
Now, lets change the useEffect
to this:
useEffect(() => {
console.log(counter);
}, []);
Notice that we're now passing an empty array "[]" (also known as Dependency Array) as the second argument to the useEffect
hook
Running the code again, I tap on the increment button 3 times & these are the console log I get:
Okay, so this time the useEffect
ran only at the time of mount of the component & NOT at the time of state update... INTERESTING! π§
Now, again! let us edit the code once more, bear with me :)
//App.js
import { useEffect, useState } from "react";
export default function App() {
const [counter, setCounter] = useState(0);
const [anotherCounter, setAnotherCounter] = useState(0)
useEffect(() => {
console.log("COUNTER VALUE", counter);
}, [counter]);
return (
<div style={{ width: "100%", textAlign: "center" }}>
<h3>{counter}</h3>
<button onClick={() => setCounter((prevValue) => prevValue + 1)}>
Increment
</button>
<br /><br />
<h3>{anotherCounter}</h3>
<button onClick={() => setAnotherCounter((prevValue) => prevValue + 1)}>
Random State Update
</button>
</div>
);
}
Alright, so... I've done 3 things here:
- Add the
counter
state variable to the previously left empty array - Add another state variable & called it
anotherCounter
- Add the JSX to display & increment the
anotherCounter
variable
I'll now run the app & click on the "Increment" button 3 times & "Random State Update" button 2 times and these are the console logs I got
Oohoo! so now the useEffect
hook is firing our function SELECTIVELY when the counter
state is changed and not when anotherCounter
state is changed, although, the setAnotherCounter
is causing the component to re-render & update the anotherCounter
to the UI.
Now, you've probably understood to some extent what that "empty array ( [] )" meant...
NICE!
Let us recap what useEffect
when correctly written can do!
- With the dependency array left empty, the
useEffect
will run the callback function (defined by us) ONLY ONCE right after the component renders UI. (equivalent tocomponentDidMount
in class components)
useEffect(() => {
/*
Here, state initialization logic can be added
OR, fetch requests to Backend servers can be made for data-fetching
As this is only running once, you're not bombarding your server
*/
}, [])
- With state variable(s) given in the dependency array, the
useEffect
will run when the component first mounts & will also be running whenever the given state variables are changed
useEffect(() => {
// ...
}, [...dependencies])
- With no dependency array defined as its second argument, the hook will call our function on EVERY SUBSEQUENT component re-render
useEffect(() => {
// I don't prefer this but yeah! it's there if you want to use it!
})
Aaaand... that's a wrap!
Like this post, if you liked it π
But if you loved it? man you gotta follow me on Twitter π
Feedback is much appreciated! π€
Top comments (1)
You forgot another important use-case that is the 'return use case'.
You can use return to execute code that will be executed every time the component is about to destroyed.
This is most useful if you have created an event listener in useEffect and want it to be destroyed before the component is rerendered or else multiple event listeners might be created.