React Hooks: useEffect
Tags: react, hooks, javascript, beginners
Published: 2020-08-21 20:17:03

Before talking about the `useEffect` Hook, let me ask you: Have you ever tried to do something like:
```javascript
 document.getElementById("someId").classList.add("someClass");
```
and then found out that React was now complaining `TypeError: Cannot read property 'classList' of null`?

Well, I most certainly have.

At first, I would be like, "*What’s going on now? I have a div element with `someId` just right there!!!*" And then I would realize, "*Aha, React has not finished positioning all my HTML elements on the page yet. It does not know about the div with `someId` yet. So my div is null to React.*"

Now, let's talk about `useEffect()`.

When I think of `useEffect()`, I imagine it as the first door that React knocks on, the moment it is done with positioning all HTML elements on the page. And therefore, it is where we should put the code that we want to run right after that happens so that React never complains about missing elements.  

Since React renders every time a change happens to a component, it knocks on/calls `useEffect()` after every render.

The simplest structure of `useEffect` looks like this:
```javascript
useEffect( ()=>{
     // code to run after every render/re-render
});

```
Let's see a simple example.

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

const App =()=> {
  let [circleBackgroundColor, setCircleBackgroundColor] = useState("lightblue");
    
  useEffect(()=>{
       // change the bg-color of the body after every render
       document.body.style.backgroundColor = getRandomColor();
    })
    
    const changeCircleBackgroundColor = ()=>{
      // change the value of circleBackgroundColor
      setCircleBackgroundColor(getRandomColor())
    }

    return(
      <main>
          <div 
            style={{backgroundColor:circleBackgroundColor}} 
            className="circle"
          />
          <button onClick={changeCircleBackgroundColor}>Change Color</button>
      </main>
    )
}

const getRandomColor = ()=>{
   return "#" + Math.random().toString(16).slice(2,8);
}
```
{% codepen https://codepen.io/hajarnasr/pen/WNwwBdy default-tab=js,result %}

Notice here that the bg-color of `body` changes not only on refreshing the page but also on changing the background color of the circle div.

Why does that happen?

*On refreshing the page*, the bg-color of `body` changes because React calls `useEffect` right after the first render.

*On changing the bg-color of the circle div*, the bg-color of `body` changes because when changing the state variable `circleBackgroundColor`, React has to re-render `App`. And after re-rendering, it calls `useEffect` again. 

But wait, is that even efficient?
I mean, calling `useEffect` every time a tiny, little change happens to a component, is that efficient?

No, it is not. And most of the time, we would not want that to happen.

`useEffect` takes a second optional parameter called the dependency array. We pass the dependency array to `useEffect` to tell React not to bother calling `useEffect` every time a change happens to a component. To tell React that it just needs to keep an eye on the dependency array variables and that it needs to call `useEffect` only when any of these variables change.

So that's how `useEffect` should look like now:
```javascript
useEffect(()=>{
     // code to run only on the first render and when depency1 or/and depency2 changes.
}, 
// the dependency array
[depency1, depency2]);
```
Let's make a few changes to our last example and pass a dependency array to `useEffect`.

```javascript
...
// onOff will be a dependency to useEffect
let [onOff, setOnOff] = React.useState(true);
...
useEffect(()=>{
       document.body.style.backgroundColor = getRandomColor();
    // add onOff in the dependency array
    }, [onOff])
}
...
 // just a button to toggle the value of onOff
 <button onClick={()=>setOnOff(!onOff)}>toggle onOff</button>
```
If a component has `useEffect`, the first thing React does after the first render is to call `useEffect` -- whether it has a dependency array or not. After that, React gets to know if `useEffect` has a dependency array or not. And based on that knowledge React has to, well, react. 

If there is not a dependency array passed to `useEffect`, React has to remember to call `useEffect` after all future renders. 

If there *is* a dependency array passed to `useEffect`, React has to keep an eye on all these dependencies. And after any future changes happen to any of them, React will have to call `useEffect`. 

Let's see that in code.

{% codepen https://codepen.io/hajarnasr/pen/ExKNPPZ default-tab=js,result %}

In this example, when React calls `useEffect` after the first render, it knows that there is an `onOff` dependency passed to it. So, it will then remember that it will not have to call `useEffect` unless `onOff` changes.

Hence, the bg-color of `body` changes only on the first render and every time `onOff` changes. 

Notice that changing the bg-color of the circle `div` does not affect `body` anymore. React no longer calls `useEffect` after changing `circleBackgroundColor`.

So now,  we have limited the calls to `useEffect` from "*after every render/re-render*" to "*only after the first render and all the re-renders caused by the changes of the dependencies*."

What if we want to limit the calls to even less than that? 
What if we want to run `useEffect` only once, after the first render?

We can achieve that by passing an empty array as the dependency array to `useEffect`. By doing that, React will call `useEffect` after the first render and then forget all about it. It will never call it again.

**Note**: You have to be careful when using `[]` as the dependency array. Make sure that `useEffect` does not contain any variables (`state` or `props`) or functions from inside its component. Refactor and strip `useEffect` of all the variables and functions from inside its component before using `[]` as the dependency array, or you have to list them as dependencies.

One last thing to mention on this subject is that we can return a function at the end of `useEffect`. That function runs after every re-render (just right before the new effect runs), to clean up after the effect from the last render. It also runs right before the component is removed from the DOM. 

If we change a little bit in the code form the earlier example:
```javascript
    useEffect(()=>{
      // add a delay to see the effect of the clean-up function
      setTimeout(()=>{  
       document.body.style.backgroundColor = getRandomColor()
       }
      , 500);
       // the clean up function
       return ()=>{
          // it cleans up the bg-color of the body 
          // just right before the new effect tasks a place
          // and changes it again
          document.body.style.backgroundColor = "white";
       }
    }, [onOff])
```
{% codepen https://codepen.io/hajarnasr/pen/NWNdPqe default-tab=js,result %}

Here, when we toggle `onOff`, the cleanup function runs to clean up the bg-color of `body` before the new effect runs and changes it again.

That's it.
Thank you for reading.