A Complete Beginner's Guide to useEffect Hook [Part 3]
Tags: javascript, react, reacthooks
Published: 2022-03-23 15:10:06


## Introduction






> `useEffect` is a React Hook that is used to handle side effects in React functional components. Introduced in late October 2018, it provides a single API to handle `componentDidMount`, `componentDidUnmount`, `componentDidUpdate` as what was previously done in class-based React components.





## What is useEffect Hook?

According to React's official doc :

**_"The Effect Hook, useEffect, adds the ability to perform side effects from a functional component"_**


But what are these side effects that we are talking about?
Well, it means we need to do something after the component renders such as data fetching, changes to the DOM, network requests. These kinds of operation are called effects and can be done using the `useEffect` hook. 

A `useEffect` hook takes in two parameters, a callback function and a dependency array respectively.

```js
const callbackFunction = () => {  }
dependencyArray = [value1, value2, value3, ...]

useEffect(callbackFunction, dependencyArray)
```

Or quite simply the above can be summed together and usually what we see in codebases:

```js
useEffect( () => {}, 
  [value1, value2, value3, ...]
)
```


## useEffect in action :
Suppose we have a counter button that increases the count by 1 when clicked :

```
function App() {
 const [count, setCount] = React.useState(0)
 return (
  <div>
    <p>{count}</p>
    <button onClick={() => setCount(count + 1)}>click</button>
  </div>
);
}

ReactDOM.render(<App />, document.getElementById("root"));

```
What if I want this count value to get dynamically reflected on the page title (i.e. next to the favicon icon), for every button click?

Now, this does sound like we have to handle an effect triggered by the component, hence a perfect use case for the useEffect hook.

Let's import useEffect at the top and call the hook inside the component (just as we did for useState hook).
useEffect takes in two arguments, a callback function to trigger and a dependency array, which we will about later in this post :

```
function App() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    document.title = count;
  });

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

```



Here's how the above React component will behave :

* The `App`  functional component will return the HTML and render it to the screen with an initial count of 0, set by the useState hook.
* Immediately, the `useEffect` hook runs asynchronously and sets the `document.title` to the initial count i.e. 0.
* The rule of thumb is, whenever something inside the component changes(say, click of a button!), the `App` component will re-render itself with an updated value.
* Suppose we click the `increment` button setting the count value from `0` to `1`, It will force the `App` component to re-render, now with the updated value. useEffect will run asynchronously setting the title to the updated value of count that is `1`




















## Adapting to Correct Mental Model :

While the `useEffect` hook seems easy to implement when working with isolated demo components, it is highly likely to run into issues when dealing with large codebases.
The reason being, poor understanding of underlying concepts and continuous comparison with class-based React lifecycle methods.

Back in the day, when we were using class-based components (no issues if you haven't!), the component side-effects were handled using Lifecycle Methods, and useEffect hook somewhat does the same thing what 
`componentDidMount`, `componentDidUpdate` and `componentWillUnmount` APIs did in Lifecycle methods, but they do differ in how the things get handled. Applying Lifecycle's mental model to hooks could result in unnecessary & unexpected behaviour.

To truly grasp useEffect, we have to "unlearn" the lifecycle way of doing things, as quoted by Dan Abramov,


** _"It’s only after I stopped looking at the useEffect Hook through the prism of the familiar class lifecycle methods that everything came together for me."_ **

Let's create a ** class-based ** component first,

```js
class App extends React.Component {
 state = {
  name: ""
 };

componentDidMount() {
  setTimeout(() => {
    console.log("MOUNT", this.state.name);
  }, 3000);
}

render() {
 return (
  <div>
    <input
	value={this.state.name}
	onChange={(event) => this.setState({ name: event.target.value })}
    />
  </div>
 );
 }
}
```


As you can see the `console` message fires after 3s, what if in between those 3 seconds, we type something to the `<input />` field?
Will the `componentDidMount` print empty `this.state.name` or would it capture the latest value from the input component?

The answer is, it would capture the latest value, the reason being how lifecycle methods work in a class-based component. 

render method creates a DOM node -> componentDidMount is called -> State is updated -> DOM is re-rendered fetching the latest value from state.

Now if we translate the same code to a hook based functional component, it works totally different. The functional component returns a HTML node making initial state value to be empty on the very first mount.

`useLayoutEffect` is another hook that can replicate the class-based example more accurately. Kent C Dodds explains very well when to use each in this [post](https://kentcdodds.com/blog/useeffect-vs-uselayouteffect/)

Play around with the code here [here](https://codepen.io/abhinav-anshul/pen/jOBBmPj)



















## Dependency Array :

The second parameter for `useEffect` is a dependency array. It is an array of all the values on which the side-effect should run/ trigger itself.

For example, let's see this counter component, where when a button is clicked the count value increments by 1, with the help of `useState` hook.

```js
function App(){
	
 const [count, setCount] = React.useState(0)
 React.useEffect(() => {console.log("Running Effect")})
 handleChange = () => setCount(prev => prev + 1)
	
	
 return(
  <div>	
    {console.log("COMPONENT RE-RENDER")}
    <h1>Hello</h1>
    <button onClick={handleChange}>click</button>
  </div>
 )
}


ReactDOM.render(<App />, document.getElementById('root'))


```
 Now, what can we learn from the above example? As we can notice, there is an `useEffect` hook with no second argument. This would result in re-rendering of the `App` component whenever a value inside changes, in this case, the `count` value is changing. Hence for every button click the component will keep on re-rendering itself, printing `COMPONENT RE-RENDER` to the console.

** How do we prevent this? **

By adding a second argument to the `useEffect` hook.

```js
function App(){
	
 const [count, setCount] = React.useState(0)
 React.useEffect(() => {console.log("Running Effect")}, []) 
 handleChange = () => setCount(prev => prev + 1)
	
	
return(
<div>	
  {console.log("COMPONENT RE-RENDER")}
  <h1>Hello</h1>
  <button onClick={handleChange}>click</button>
 </div>
  )
}

```

At the very first mount, we will see both the logs to the console,

```js
Running Effect
COMPONENT RE-RENDER

```
But this time, as we click on the button there won't be any log from the `useEffect` hook as the empty array makes sure to run it just once and all the subsequent logs will be from `App`

```js
Running Effect
COMPONENT RE-RENDER
COMPONENT RE-RENDER  // keep logging as many times as the button clicks


```

Let's go a step further and try filling in the dependency array list with `count` value as :

```js
React.useEffect(() => {console.log("Running Effect")}, [count])
```
This time things get interesting as it logs both the console text.

```js
Running Effect
COMPONENT RE-RENDER
Running Effect
COMPONENT RE-RENDER
... // keep logging both the text for button clicks

```
The first text("Running Effect") is rendered as the effect is triggered whenever the array item modifies(count as mentioned there) and it does for button clicks.

while the second text("COMPONENT RE-RENDER") is very much expected as the value inside the component itself is changing, so naturally, it has to re-render to update the DOM with the latest value.

[codepen](https://codepen.io/abhinav-anshul/pen/JjWqPKE) 










## Incorrect Dependency Array :
It is worth mentioning that incorrect use of dependency array items could lead to issues that are harder to debug. React team strongly advises to always fill in items in the array and not to leave them out.

There is a very helpful `exhaustive-deps` ESlint rule which helps us in issues such as `stale closure` which might be due to incorrect dependency or even several other reasons and helps us auto-fix it.
Read more in-depth about the announcement [here](https://github.com/facebook/react/issues/14920)











## useEffect with cleanup function :

As we have read earlier in this post, `useEffect` expects either an undefined or an optional cleanup function as its return value. A Cleanup function can be thought of as a way to clear out the side effects when the component unmounts.

```js
useEffect(() => {
  // side effect logic here
})

// cleanup function
return () => {
  // logic
}
```

Let's see cleanup function into action into a very contrived example below:


```js
function App() {
  const [number, setNumber] = useState(0);

  useEffect(() => {
    console.log("number is", number);
    return () => {
      console.log("running cleanup function");
    };
  }, [number]);

  return (
    <div className="App">
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />

      <p>{number}</p>
    </div>
  );
}
```


A Cleanup function is used in a very small number of use cases such as clearing out timers, cleaning unnecessary event listeners, unsubscribing to a post etc. If not sanitized properly, they could lead to something called a [Memory leak](https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/) in JavaScript.













## Batching multiple useEffects :

What's best, putting different side-effect into one `useEffect` hook or in multiple?
Honestly, it depends on the use case and how we are interacting with various components. One important thing to note here is that react will apply effect in the order they were written(in case we have multiple useEffect hooks)

It is perfectly fine to do this in a single component :

```
useEffect(() => {
// Second side effect 
})

useEffect(() => {
// First side effect
})

```










## Conceptual Pitfalls to Avoid :

**1. ** useEffect hook does not truly mimic the `componentDidMount` lifecycle method. Same goes for `componentDidMount` & `componentDidUpdate`. While the end result might look similar when implementing, the order in which they are called and mounted is very distinctive as we've already discussed in the above point.

**2. ** The useEffect hook expects us to return a cleanup function, to unmount/clear the side-effects after a certain condition has been fulfilled, if not provided it returns `undefined`. We have to make sure not to return anything else when dealing with an `async` function, as an asynchronous function returns a promise.

The following code is ** wrong ** as it returns an unexpected promise from useEffect Hook

```
const App = () => {   
  useEffect(async () => {
    const unsubsribe = await subscriberFunction();    
	return () => {
       unsubscribe()
     }
   }, []) 
return <div></div>;
}
```

Now, there are various ways to deal with an `async` function inside a `useEffect` hook. we can use `IIFE` style technique such as :

```
const App = () => {
  useEffect(() => {
    
    async function subscriberFunction() {
      await fetchIds();
    }   
    subscriberFunction();
  }, []);
return <div></div>;
};

```
 **3. ** The order in which useEffect has been specified in a component matters while invoking.













## Wrapping Up : 

React `useEffect` hook deviates itself from the class-based lifecycle approach. 
It takes time and practice to grasp `useEffect's` best patterns and foundational concepts, which when used correctly, can prove to be incredibly powerful for handling side-effects in React Applications.



** Some Important Resources that I have collected over time: **

- [https://overreacted.io/a-complete-guide-to-useeffect/](https://overreacted.io/a-complete-guide-to-useeffect/)
- [https://stackoverflow.com/questions/53253940/make-react-useeffect-hook-not-run-on-initial-render?rq=1](https://stackoverflow.com/questions/53253940/make-react-useeffect-hook-not-run-on-initial-render?rq=1)
- [https://reacttraining.com/blog/useEffect-is-not-the-new-componentDidMount/](https://reacttraining.com/blog/useEffect-is-not-the-new-componentDidMount/)


_ Loved this post? Have a suggestion or just want to say hi? Reach out to me on [Twitter](https://twitter.com/abhhnv) _

Originally written by [Abhinav Anshul](https://blockchain.works-hub.com/users/c49b5fad-2fef-4acc-aa7a-7cd78f11c9a1?utm_source=dev_to&utm_medium=blog_xpost) for [Blockchain Works](https://blockchain.works-hub.com/learn/a-complete-beginners-guide-to-useeffect-hook-83b07?utm_source=dev_to&utm_medium=blog_xpost)