Understanding the React useEffect() Hook
Tags: react, webdev, javascript, programming
Published: 2021-04-23 20:18:56

## Introduction

In the [previous post](https://dev.to/raunaqchawhan/understanding-the-react-usestate-hook-4d2d), we looked at the [useState()](https://dev.to/raunaqchawhan/understanding-the-react-usestate-hook-4d2d) Hook that adds state to a functional component. We learned how to initialize, update and access state variables in a functional component using useState() Hook.

In this post, we'll focus on the **useEffect()** Hook that let us perform side effects in functional components. We'll also understand how this particular hook can be used to mimic the behavior of [componentDidMount()](https://reactjs.org/docs/react-component.html#componentdidmount), [componentWillUnmount()](https://reactjs.org/docs/react-component.html#componentwillunmount) and [componentDidUpdate()](https://reactjs.org/docs/react-component.html#componentdidupdate) lifecycle methods.

## Prerequisites

- An understanding of the [useState()](https://dev.to/raunaqchawhan/understanding-the-react-usestate-hook-4d2d) Hook

## useEffect() Hook

The operations such as data fetching, manual DOM mutations, logging, setting up a subscription and unsubscribing it are all examples of side effects. These side effects are too early to be dealt while the component is being rendered to the screen. Therefore, the class components are provided with lifecycle methods such as `componentDidMount`, `componentDidUpdate` and `componentWillUnmount` that run after React has updated the DOM.

However, functional components don't have such lifecycle methods. Thus, `useEffect` Hook was introduced that let us perform side effects in functional components. 

The syntax for **useEffect** Hook is as below:

```javascript
useEffect(function, [dependencies]);

// first argument is a function where we pass our side effect
// second argument is a dependencies array. it is an optional argument
// with no dependencies array present, useEffect runs after every render
```

Now that we are clear with the syntax, lets take a look at the following class based component that logs a message to the console after the component is rendered to the screen and on any state update

```javascript
import React, { Component } from "react";

export default class App extends Component {
  
  constructor(props) {
    super(props);
    this.state = {
      age: 26
    }
  }

  componentDidMount() {
    console.log(`I am ${this.state.age} years old`);
  }

  componentDidUpdate() {
    console.log(`I am ${this.state.age} years old`);
  }

  render() {
    return (
      <div>
        <p>I am {this.state.age} years old</p>
        <button onClick={() => this.setState({
          age: this.state.age + 1
        })}>Celebrate Birthday</button>
      </div>
    );
  }
}
```

As per the above code block, after the component gets rendered to the screen, `componentDidMount` gets called which logs a message to the console. When the button is clicked, the component re-renders with the updated age value and `componentDidUpdate` gets called which logs a message to the console. 

It is evident from the above code block that duplicate code is used in both the lifecycle methods. This is because in many cases you want to perform the same side effect regardless of whether the component was just mounted or has been updated. React class components don't have a lifecycle method that allows a particular code to run after every render.

Now, lets take a look at the **functional component** using **useEffect** Hook to achieve the same thing

```javascript
import React, { useState, useEffect } from "react";

export default function App() {

  const [age, setAge] = useState(26);

  useEffect(() => {
    console.log(`I am ${age} years old`);
  });

  return (
    <div>
      <p>I am {age} years old</p>
      <button onClick={() => setAge(age + 1)}>Celebrate Birthday</button>
    </div>
  );
}

```

The above code performs the same thing that the class component does but with lesser code. Here, we use the **useState** Hook to initialize and update the age variable.

Now, lets understand the `useEffect` Hook

- In order to perform side effects in functional component, you first need to import `useEffect` Hook from React.

- In the App component above, you can see that the State Hook is used to initialize the `age` variable. 

- `useEffect` Hook is defined after the State Hook and a function to log the `age` variable is passed to it.

- The Effect Hook is defined inside the component so that it can easily access the `age` variable or any `props` passed to the component.

- After React renders the component to the screen, it moves to the `useEffect` Hook and runs it which logs the `age` variable to the console.

- When you click the button, `age` variable is updated that leads to re-rendering of the component with the updated value. This triggers the effect to run again.

- The `useEffect` Hook runs both after the first render and after every update (on state variable change and props change) because there is no dependencies array present as the second argument.

Thus, you can see that the functional component with `useEffect` Hook is able to achieve the same thing in a single code block which class component achieved in two lifecycle methods.

Now, you got a basic understanding of how `useEffect` Hook runs. But, without the **dependencies array**, it is seen that the Effect Hook runs after every render. 

There are cases where we don't want it to run after every render as it can lead to undesirable results or performance issues in many cases. In such scenarios, you can make use of dependencies array to determine when `useEffect` should run again once it has run after the first render. Any change in value of dependencies present in the array triggers the `useEffect` Hook to run again.

## useEffect() with dependencies array

The following code block introduces the dependencies array in the Effect Hook

```javascript
import React, { useState, useEffect } from "react";

export default function App() {
  
  const [age, setAge] = useState(26);
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`I am ${age} years old`);
  }, [age]);

  return (
    <div>
      <p>I am {age} years old</p>
      <button onClick={() => setAge(age + 1)}>Celebrate Birthday</button>
      <p>The guest count is {count}</p>
      <button onClick={() => setCount(count + 1)}>Add Guest</button>
    </div>
  );
}
```

Here, we are having two state variables `age` and `count`. The dependencies array has `age` variable present in it. So, once the Effect hook runs after the first render, it will now run only when the `age` variable is updated. Therefore, if you click the button that updates the `count` variable,  it will not trigger the effect to run. But when the button that updates the `age` variable is clicked, the effect will run. Therefore, the effect now runs only when `age` is updated and not after every render.

So far, you've looked at the side effects without cleanup. But there are certain side effects which do require clean up. Some examples include setting up a subscription to some external data source which also needs to be cleaned up so that no memory leak is introduced or setting up a timer and then clearing it after that component is destroyed.

## useEffect() with cleanup

Now, lets take a look at the class component where setting up a timer is typically done in `componentDidMount` method and clean up is done in `componentWillUnmount` method

```javascript
import React, { Component } from "react";

export default class App extends Component {
  
  constructor(props) {
    super(props);
    this.state = {
      timer: 0
    }
  }

  componentDidMount() {
    this.id = setInterval(() => {
      this.setState({
        timer: this.state.timer + 1
      })
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.id);
  }

  render() {
    return (
      <div>
        <p>Timer: {this.state.timer}</p>
      </div>
    );
  }
}
```

`componentDidMount` gets executed after the component is rendered to the screen thereby setting up a timer. This timer continues to run till the component is in scope. If the component is about to be unmounted and destroyed, `componentWillUnmount` gets executed immediately before unmount and any necessary cleanup is performed such as clearing up the timer in the above example.

Now, lets take a look at an equivalent functional component. The function passed to the useEffect hook can return a function that acts as a clean up script. This script runs when the component is about to be unmounted and before every consecutive run of the Effect hook after the first run.

```javascript
import React, { useState, useEffect } from "react";

export default function App() {
  
  const [timer, setTimer] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setTimer(prevValue => prevValue + 1)
    }, 1000);
    return () => {
      // cleanup script
      clearInterval(id)
    }
  },[]);

    return (
      <div>
        <p>Timer: {timer}</p>
      </div>
    );
}
```

In the above example, the Effect hook returns a cleanup function. Since, the dependencies array is empty, the effect does not depend on any changes in the state value or props value and therefore it never re-runs. It will always have the initial value of state and props.

Since the Effect hook is restricted here to run only once, the clean up script gets executed only when the component is about to be unmounted. Therefore using the Effect Hook in this manner is equivalent to `componentDidMount` and `componentWillUnmount` lifecycle methods.

*You can have more than one Effect Hook in your component.*

## Conclusion

In this post, you got an understanding of the **useEffect()** Hook. You learned its syntax and how it is used to perform side effects in a functional component. You also learned about the dependencies array that restricts the Effect hook to run on every render. You learned how related code gets split between lifecycle methods in class component whereas Hooks in functional component let us split the code based on what it is doing and groups related code together.

___

Thanks for taking the time to read this post. I hope this post helped you!!😊😃 If you liked it, **please share.**

It would be great to connect with you on [Twitter](https://twitter.com/_raunaq_). Please do share your valuable feedback and suggestions👋