DEV Community 👩‍💻👨‍💻

Cover image for The useEffect hook in ReactJS
Kuvam Bhardwaj
Kuvam Bhardwaj

Posted on

The useEffect hook in ReactJS

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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:

  1. When the component is first mounted ( printed "0" to the console )
  2. When I tap the increment button or in short, the state update occurred

Now, lets change the useEffect to this:

useEffect(() => {
  console.log(counter);
}, []);
Enter fullscreen mode Exit fullscreen mode

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

Alright, so... I've done 3 things here:

  1. Add the counter state variable to the previously left empty array
  2. Add another state variable & called it anotherCounter
  3. 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!

  1. 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 to componentDidMount 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

  */
}, [])
Enter fullscreen mode Exit fullscreen mode
  1. 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])
Enter fullscreen mode Exit fullscreen mode
  1. 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!
})
Enter fullscreen mode Exit fullscreen mode

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! 🤗



Meet you another day with another post ⚡️

Latest comments (1)

Collapse
 
themubashir919 profile image
Muhammad Mobashir

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.

🤯

"I made 10x faster JSON.stringify() functions, even type safe"

☝️ Must read for JS devs