Understanding React's useEffect Hook
Tags: react, webdev, javascript
Published: 2020-04-15 02:42:04

<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*x4_036pvFl4Pwopb" /><figcaption>Photo by <a href="https://unsplash.com/@sickhews?utm_source=medium&amp;utm_medium=referral">Wes Hicks</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>React Hooks are awesome and definitely make our lives as React developers much easier. In case you don’t know what they are, Hooks<strong> </strong>were<strong> introduced in React 16.8.0</strong>, and they let you <strong>use state and other React features without writing a class </strong>💃</p><p>The most important thing is that<strong> Hooks don’t replace your knowledge of React concepts.</strong> Instead, Hooks provide a <strong>more direct API</strong> to the React concepts you already know: props, state, context, refs, and lifecycle.</p><blockquote>Note: if you’re new to React, learn Hooks and functional components first. Only after that, learn the old way of doing things (class components)</blockquote><p><strong>This post is not about Hooks</strong> in general, but if you want to know more about them and why they exist, the best thing you can do is watching the official <strong>React Hooks presentation</strong>:</p>
{% youtube dpw9EHDh2bM %}
<p>If you haven’t learned Hooks yet, you should do it as soon as possible. And if you’ve been reading about them, you may be a bit <strong>confused about the </strong><strong>useEffect Hook 😕</strong></p><h2>The useEffect Hook</h2><p>Plain and simple, useEffect lets you perform <strong>side effects</strong> in function components.</p><p><em>Ok… But what are side effects? </em>😒</p><p>Side effects are all the <strong>operations that affect your component and can’t be done during rendering</strong>. Things like <strong>fetching data, subscriptions or manually changing the DOM</strong> are all examples of side effects, and things you’ve most likely done in the past.</p><h3>What are the benefits? ✅</h3><ul><li>Avoiding duplication of code</li><li>Bringing related code closer together</li><li>Avoiding bugs</li><li>Separating separate effects by their purpose, something impossible to do with classes</li></ul><p>All these benefits will become clearer once you read the following explanation and examples.</p><h3>The old way of doing things 🔙</h3><p>In the past, when we only had class components, we handled side effects using the <strong>lifecycle methods.</strong></p><p>For example, among other stuff, we used <strong>componentDidMount</strong> to fetch data from APIs, <strong>componentDidUpdate</strong> to send data when something changed, and <strong>componentWillUnmount</strong> to unsubscribe from events.</p>


```javascript
class MyComponent extends React.Component {
  constructor() {}
  componentDidMount() {
    // Fetch data from API
  }
  componentDidUpdate(prevProps, prevState) {
    // Send data when props change
  }
  componentWillUnmount() {
    // Unsubscribe from events before the component is removed
  }
  render() {}
}
```

<p>useEffect<em> </em>combines all those three methods into one convenient API.</p><h3>A paradigm shift ⚡️</h3><p>To really understanduseEffect, we need to <strong>change the way we think about components and state changes.</strong></p><p>When we used <strong>classes</strong>, we had to think about <strong><em>when</em></strong>. The question we needed to answer was <strong><em>when does this effect run?</em></strong></p><p>useEffect is different.</p><blockquote><strong>It exists to synchronize the state of the world with the state of the component.</strong></blockquote><p>So the question now is: <strong><em>which state does this effect synchronize with?</em></strong></p><p>You need to stop thinking about lifecycles and time, and <strong>start thinking about state and synchronization with the DOM.</strong></p><h2>How does useEffect work? ⚙️</h2><p>By default, useEffect<em> </em><strong>runs after every render, including the first one. React guarantees the DOM has been updated </strong>by the time it runs the effects.</p><p>When you use useEffect, you’re <strong>telling React that your component needs to do something after rendering</strong>. You pass a <strong>function</strong> (the “<em>effect</em>”), and React will <strong>remember it and call it later</strong>, after performing the DOM updates.</p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // this side effect will run after every render
  });
  return ...
}
```

<h3>What about cleanups? 🗑️</h3><p>We said earlier that useEffectalso “replaces” <strong>componentWillUnmount</strong> method.</p><p>A common use case for this method is to <strong>unsubscribe</strong> from events <strong>before the component is finally unmounted.</strong></p><p>To replicate that behavior with useEffect, you just need to <strong>return a function. </strong>React will take care of everything and <strong>run it when it is time to clean up.</strong></p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // this side effect will run after every render
    return function cleanup() {
      // this code will run before the component is unmounted
    }
  });
  
  return ...
}
```

<h3>The dependencies array</h3><p>Of course, <strong>running all effects on every render might lead to performance issues</strong> (and even infinite loops in some cases).</p><p>However, <strong>React doesn’t know what your function does</strong> before calling it. It may seem obvious to you that it’s not necessary to run some effect function again, but not for React.</p><p>So to fix that, you need to <strong><em>tell</em></strong> React. You can provide a second argument to useEffect, which is a <strong>dependencies array.</strong></p><p>Think of it like telling React <strong><em>“This function only needs to run if X is updated</em></strong><em>”. </em>If each of these values is the same between the current and the previous time this effect ran, there’s <strong>nothing to synchronize</strong> and React will <strong>skip the effect.</strong></p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // this side effect will run only when value1 changes
  }, [value1]);
  
  return ...
}
```

<p>If you pass an <strong>empty array</strong> as second argument, the function will <strong>run just once, after the first render</strong>. This tells React that your effect <strong>doesn’t depend on any values</strong> from props or state, so it <strong>never needs to re-run.</strong></p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // this side effect will run just once, after the first render
  }, []);
  
  return ...
}
```

<p>So a summary would be:</p><ul><li><strong>No dependencies array</strong>: the function runs after <strong>every render</strong></li><li><strong>Empty dependencies array</strong>: the function runs only after<strong> </strong>the<strong> first render</strong></li><li><strong>Dependencies array with some values</strong>: the function runs only if any of those values <strong>change</strong></li></ul>
<p>If you use an <strong>empty dependencies array</strong>, don’t do it because you think “I need this to run just once, after the first render”; do it because you know that <strong>the stuff it’s doing will never get stale.</strong></p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // this side effect will run just once, after the first render
  }, []);
  React.useEffect(() => {
    // this side effect will run only when value1 changes
  }, [value1]);
  React.useEffect(() => {
    // this side effect will run after every render
  });
  return ...
}
```

<h3>Be careful with your dependencies array ⚠️</h3><p>If you use the dependencies array, make sure it includes <strong>all values from the component scope (such as props and state) that change over time and that are used by the effect</strong>. It’s very common to forget a value or to think that you don’t need it in the array. If you do that, you will produce bugs, because your code will reference stale values from previous renders.</p><p>To help with this, always use <a href="https://www.npmjs.com/package/eslint-plugin-react-hooks"><strong>eslint-plugin-react-hooks</strong></a><strong>.</strong></p><h3>You can use multiple effects! 😏</h3><p>You can calluseEffect <strong>as many times as you want</strong>. In fact, it’s recommended to call it several times, in order to <strong>separate concerns.</strong></p><p>For example, there are some things that you need to do only after the first render and never again, and other things that need to happen after every render.</p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // I need to do this just once, after the first render
  }, []);
  React.useEffect(() => {
    // But this other thing needs to be done after every render
  });
  return ...
}
```

<h3>Goodbye code duplication! 👋🏼</h3><p>useEffect helps to avoid code duplication in a very common case.</p><p>Let’s say you need to <strong>run an effect after every render</strong>. It doesn’t matter if the component has just been mounted or it has just been updated, you need to perform certain side effect in both cases.</p><p>To do this with <strong>classes</strong> you need to <strong>duplicate</strong> <strong>code</strong>:</p>

```javascript
class Mycomponent extends React.Component {
  constructor(props) {}

  componentDidMount() {
    // do something
  }  
  componentDidUpdate() {    
    // do the same thing again
  }
  render() {
    return ...
  }
}
```

<p>But with useEffect, the code will run <strong>after every render, including the first one.</strong></p>

```javascript
function MyComponent() {
  React.useEffect(() => {
    // do something
  });
  return ...
}
```

<h3>Keep related code together 👫</h3><p>Let’s say we have a component where we need to subscribe to an event, and then unsubscribe from it before the component is removed. Here’s how we would do that with classes:</p>

```javascript
class Mycomponent extends React.Component {
  constructor(props) {}
  componentDidMount() {
    // susbscribe to event X
  }
  componentWillUnmount() {    
    // unsubscribe from event X
  }
  render() {
    return ...
  }
}
```

<p>Note how<strong> lifecycle methods</strong> force us to <strong>split</strong> this logic, even though the code in both of them is <strong>related to the same effect.</strong></p><p>With useEffect, we can keep this code together in the same function:</p>

```javascript
function MyComponent() {
  useEffect(() => {
    // subscribe to event X
    return function cleanup() {       
      // unsubscribe from event X
    };  
  });
  
  return ...
}
```

<h3>useEffect(fn, []) vs. componentDidMount 🥊</h3><p>For what I’ve told you so far, you may think that’s right. I’ve told you that <strong>if the dependencies array is empty, your effect will run only after the first render, </strong>which is how componentDidMount works, right?</p><p>Well, <strong>there is a difference.</strong></p><p>useEffect runs <strong>after</strong> the paint has been committed to the screen. componentDidMount runs <strong>before</strong> the paint has been commited to the screen.</p><p><strong>For most cases, you won’t need to worry</strong> about this, because most of your effects will be <strong>asynchronous</strong>.</p><p>But there are some things that require that you read the DOM and <strong>synchronously re-render</strong>. For example, if you need to measure the layout.</p><p>For these cases, there is a <strong>separate </strong><a href="https://reactjs.org/docs/hooks-reference.html#uselayouteffect"><strong>useLayoutEffect</strong></a><strong> Hook</strong> with an API identical to useEffect.</p><h2>That’s all!</h2><p>useEffect can be a little tricky, and it requires some practice. I hope this guide helps, and always remember: <strong>don’t think about lifecycles, think about synchronizing side effects to state.</strong></p><p>If you need more information, you can check <a href="https://reactjs.org/docs/hooks-effect.html">React’s official documentation</a> or <a href="https://overreacted.io/a-complete-guide-to-useeffect/">this awesome guide</a> by <a href="https://twitter.com/dan_abramov">Dan Abramov</a>.</p><p>Thanks for reading ❤️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f00cfbfbb7e" width="1" height="1">