React's useEffect Hook

React's useEffect Hook

What it does, how it works, and what to use it for

Initially, coming from a class-based React context, I had a hard time understanding hooks.

And the hook I had the hardest time with was useEffect.

Gladly, I understood it, and I now want to show you what useEffect is and how you can use it.


A quick hook introduction

Hooks were added to React in 16.8 and enable us to write functional components while still using state and other React features like lifecycle methods without a need for classes.

Some hooks also enable you to set state in functional components. This doesn't sound like much, but unlike class components, where you had to modify your component's state for React to notice, you need to tell React when something changed in functional components, too.


The useEffect hook

useEffect is a hook meant to be used when you want to perform side effects.

Manually changing the DOM or fetching data are examples of this.

By default, this hook runs after each render, which means that every time React sees the need to rerender.


Use cases of useEffect

If you are aware of React's class-style lifecycle methods:

useEffect is a replacement for

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

If you aren't aware because you started after 16.8, don't worry. useEffect is great if you want to do something when your component:

  • first renders
  • is updated/rerendered
  • is about to be cleaned up

Additionally, useEffect doesn't block the browser when it runs, unlike the old class-style lifecycle methods. This usually makes your app feel more responsive, especially when you run more logic within the hook.

Using useEffect

Running when mounted and when the component is rerendered

The classical use case of this hook that you also find within the official documentation is updating the title of your site dynamically.

In this example, your site's title is updated on every render.

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

const Component = () => {
  [notifications, setNotifications] = useState(0);

  useEffect(() => {
    document.title = `Oliver - {notifications} pending notifications`;
  });

  // ...

  return (
    // ...
  );
};

When used like this, the hook takes the place of:

  • componentDidMount
  • componentDidUpdate

Running when mounted, on rerender, and when the component is cleared up

Another use case is subscribing to an API and reacting to updates. This is an operation that usually additionally requires unsubscribing from this API before components are cleared up. Otherwise, the logic would go on running.

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

const Component = () => {
  [someState, setSomeState] = useState({});

  useEffect(() => {
    const subscription = subscribeToApi(() => {
      // ...
      setSomeState(...);
    });
    return () => {
      subscription.unsubscribe();
    };
  });

  // ...

  return (
    // ...
  );
};

When used like this, the hook takes the place of:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Running only when the component is mounted

useEffect actually takes a second argument, its dependencies. With these dependencies, you can fine-tune when useEffect is run by React.

If you want the hook to only run once, when mounted, you pass an empty array.

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

const Component = () => {
  [someState, setSomeState] = useState({});

  // This runs only once.
  // Pay attention to the
  // second argument '[]'.
  useEffect(() => {
    // ...
    setSomeState(...);
  }, []);

  // ...

  return (
    // ...
  );
};

When used like this, the hook takes the place of:

  • componentDidMount

Customizing when useEffect runs

One last option is to run useEffect every time a specific dependency or dependencies change.

Instead of an empty array, you pass the state variables you want useEffect to react to.

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

const Component = () => {
  [someState, setSomeState] = useState({});

  // This runs each time
  // someState changes
  useEffect(() => {
    // Could be an API call or whatever
    validateSomeStateCorrectness(someState);
  }, [someState]);

  // ...

  return (
    // ...
  );
};

When used like this, the hook actually doesn't react like any of the lifecycle methods. It is fired each time React notices that a state variable has changed its value.


Before You Leave

If you would love to read even more content like this, feel free to visit me on Twitter or LinkedIn.

I'd love to count you as my ever-growing group of awesome friends!