useEffect Hook in react

useEffect Hook in react

Introduction

In this article, we gonna learn the useEffect hook in detail with use cases and examples.

what is useEffect() hook?

useEffect hook is the combination and replacement of 3 life cycle methods in functional components they are

1.componentDidMount(): this method will get invoked whenever a component gets mounted in the class component.

2.componentDidUpdate(): this method will get invoked whenever the component gets updated in the class component.

3.componentWillUnmount(): this method will get invoked just before when a component is going unmount.

Syntax:

import { useEffect } from "react";

function App(){
    useEffect(()=>{   
        /*
        your code....
        */
  },["option dependency array"]);
  return(
    <>
    <h1>Welcome</h1>
    </>
  )
}
export default App;

Tip: A dependency array is optional but it is best practice to include an empty dependency array to make sure our code run without any side effects.

useEffect() hook without any dependency array:

The useEffect() hook without any dependency array will run every time whenever any component gets mounted, gets updated and gets unmounted.

let's look at some code examples to understand it much better.

useEffect() getting invoked when the component gets mounted.

import { useEffect } from "react";

function App() {
  useEffect(() => {
    console.log("useEffect Method is called");
  });
  return (
    <>
      <h1>Welcome</h1>
    </>
  );
}
export default App;

output:

if we observe the above code useEffect is invoked twice since we are in strict mode for debugging purposes but in production, it will be called once after we run npm run build command it will remove the strict mode.

The useEffect hook got invoked by react after the App component gets mounted.

useEffect() getting invoked when the component gets updated.

Note: A component will get updated or rerendered whenever its state or props get updated.

Note: for props that are going to be sent by the parent component it must be a state variable otherwise useEffect hook will not run.

state example:

In the below code whenever the user clicks on the button the state of the count variable gets updated therefore react will rerender the App Component which makes the App component gets updated, therefore, the useEffect hook will get invoked by react.

import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("useEffect Method is called");
  });
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button onClick={() => setCount((count) => count + 1)}>Click</button>
    </>
  );
}
export default App;

Output:

before clicking the button:

after clicking the button:

props example:

For this, we need 2 components, let's make 2 components one is the User and the other is the App.

where the App component is the parent component that sends userid to the user component which is a child component of the App and lets increase the userid by 1 whenever the user clicks on the button.

App.js


import { useState, useEffect } from "react";
import User from "./components/User";

function App() {
  const [userId, setUserId] = useState(0);
  useEffect(() => {
    console.log("useEffect Method inside App");
  });
  return (
    <>
      <h1>Welcome to App Component</h1>
      <br />
      <User userId={userId} />
      <button
        onClick={() => setUserId((userId) => userId + 1)}
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

User.js

import { useEffect } from "react";

function User(props) {
  useEffect(() => {
    console.log("useEffect Method inside User");
  });
  return (
    <>
      <h1>Userid is {props.userId}</h1>
    </>
  );
}
export default User;

Output:

before clicking the button

after clicking the button

useEffect() hook getting invoked when the component gets unmounted.

let's make a toggle button that hides and shows the user component whenever the user clicks on the button. let's make use of conditional rendering to do that.

Note: for conditional rendering, we can either use the if-else block or ternary. In this example, we will be using ternary for simplicity

In the below code user component will hide/be shown whenever the user clicks on the toggle button.

App.js


import { useState, useEffect } from "react";
import User from "./components/User";

function App() {
  const [displayUser, setDisplayUser] = useState(true);
  useEffect(() => {
    console.log("useEffect Method inside App");
  });
  return (
    <>
      <h1>Welcome to App Component</h1>
      <br />
      {displayUser ? <User /> : ""}
      <button
        onClick={() => setDisplayUser((displayUser) => !displayUser)}
        style={{ backgroundColor: "yellow" }}
      >
        Toggle
      </button>
    </>
  );
}

User.js

import { useEffect } from "react";

function User() {
  useEffect(() => {
    console.log("useEffect Method inside User");
  });
  return (
    <>
      <h1>Welcome to User Component</h1>
    </>
  );
}
export default User;

Output:

Before clicking on the button

After clicking on the button:

Note: After clicking on the toggle button the User component is unmounted and we can see that App component useEffect is being called but not the User component useEffect because we need to return console.log by wrapping in a call back function to make it work like below:

import { useEffect } from "react";

function User() {
  useEffect(() => {
    return () => console.log("useEffect Method inside User");
  });
  return (
    <>
      <h1>Welcome to User Component</h1>
    </>
  );
}
export default User;

Now let's reload and see the output again:

Before Clicking the button:

After Clicking the button:

The user component gets unmounted and we can see UseEffect of the User Component is getting invoked.

Again clicking on the button it will display the user component

useEffect() hook with empty [] dependency array:

if we use the useEffect hook with an empty [] dependency array in any component, it acts like the componentDidMount() life cycle method. which means it will only run once that is after the component gets mounted.

let's look at the example code given below:

import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("useEffect Method is called");
  }, []);
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button
        onClick={() => setCount((count) => count + 1)}
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

Output:

before clicking on the click button:

after clicking on the click button:

The state of the count variable is updated by increasing its value by 1 each time the user clicks on the click button, but since we are an empty [] array for useEffect hook it will not get invoked if any state/prop gets updated. This behavior is similar to componentDidMount because it will only run once when the component gets mounted the first time.

Note: Since we are in strict mode it is invoking 2 times for debugging purposes but in production, it will run only once when the component gets mounted the first time or rendered first time.

useEffect() hook with dependency array:

The useEffect() hook with array dependency will only get invoked whenever that particular state or prop which is passed inside the dependency array gets updated.

let's look at some examples to understand it.

example with the state:

let's create an App component with 2 state variables and pass one state variable inside the dependency array and create 2 buttons which on click invoke a function that updates the states of these variables by increasing it to 1.

let state variables be count and data. let's pass the count variable inside the dependency array of the useEffect hook.

useEffect hook will get invoked every time only if the user clicks on the updateCount button to update the state of the count variable by increasing its value by 1.

Note: No matter how many times we click on updateData button the useEffect hook will not be invoked as we have not passed it inside the dependency array.

import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);
  const [data, setdata] = useState(0);
  useEffect(() => {
    console.log("useEffect Method is called");
  }, [count]);
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <h2>Data is {data}</h2>
      <button
        onClick={() => setCount((count) => count + 1)}
        style={{ backgroundColor: "yellowgreen" }}
      >
        updateCount
      </button>

      <button
        onClick={() => setdata((data) => data + 1)}
        style={{ marginLeft:"5px",backgroundColor: "purple", color:"white" }}
      >
        updateData
      </button>
    </>
  );
}
export default App;

output:

before clicking on any button:

after clicking on the updateData button:

As you can see below no matter how many times user clicks on the updateData button it will not invoke the useEffect hook as we are not passing it inside the dependency array.

after clicking on the updateCount button:

As you can see below each time user clicks on the updateCount button it will invoke the useEffect hook since we have passed the count variable inside the dependency array of the useEffect hook.

example with props:

let's take two components where one component is a child and the other is a parent component which passes 2 props to the child component.

let that two-component be User and App, where User is the child component of App which gets two props from the parent component (i.e App) and let the two props be id and count which are state variables created by useState hook whose value will be increase by 1 whenever the user clicks on the button updateId and updateCount like shown below.

App.js

import { useState } from "react";
import User from "./components/User";
function App() {
  const [count, setCount] = useState(0);
  const [id, setId] = useState(0);

  return (
    <>
      <h1>Welcome to App</h1>
      <User userCount={count} userId={id} />
      <button
        onClick={() => setCount((count) => count + 1)}
        style={{ backgroundColor: "yellowgreen" }}
      >
        updateCount
      </button>

      <button
        onClick={() => setId((id) => id + 1)}
        style={{ marginLeft: "5px", backgroundColor: "purple", color: "white" }}
      >
        updateId
      </button>
    </>
  );
}
export default App;

Now let's pass userCount prop to dependency array of useEffect hook of the User component shown below.

import { useEffect } from "react";

function User(props) {
  useEffect(() => {
    return () => console.log("useEffect Method inside User");
  }, [props.userCount]);
  return (
    <>
      <h1>Welcome to User Component</h1>
      <h2>UserID: {props.userId}</h2>
      <h2>UserCount: {props.userCount}</h2>
    </>
  );
}
export default User;

Output:

before clicking on any button:

after clicking on updateId button:

Since userId prop is not passed inside the dependency array of useEffect hook it will not invoke the useEffect hook no matter how many times we click on the updateId button.

after clicking on updateCount button:

since we have passed userCount props inside the dependency array of useEffect hook of the user component it will get invoked each time when the user clicks on updateCount button.

useEffect() hook behaves like pure component:

By default, useEffect hook behaves like a pure component. which means useEffect hook will not get invoked if we update the state of the variable same as before.

example:

let us create an App component with state variable count by using useState hook and create a button that invokes a call back function that updates the state of the count variable.

let the initial state of the count variable be 0 and each time when user clicks on the button the state of the count variable always will be updated the same as the initial state value which is 0.


import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("useEffect Method is called");
  }, []);
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button
        onClick={() => setCount((count) => count)}
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

OR


import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("useEffect Method is called");
  }, []);
  return (
    <>
      <h1>Welcome</h1>
      <h2>Count is {count}</h2>
      <button
        onClick={() => setCount((count) => 0)}
        style={{ backgroundColor: "yellow" }}
      >
        Click
      </button>
    </>
  );
}
export default App;

Output:

No matter how many times the user clicks on the click button the state of the count variable is always updated to 0 therefore useEffect hook will not get invoked.

This is the default feature of useEffect hook which is very useful when we define complex callbacks inside useEffect hook. It will only get invoked when we update the state which is not the same as before which improves the performance of our application.