Linguine Code

How to replicate a React component lifecycle with useEffect

So you learned how to use React useState and how to emulate the traditional setState in a functional React component.

But now you might be scratching your head and asking yourself, “How do I emulate a React component lifecycle?”

Don’t worry, in this article will go over:

  • What is React useEffect and how to do a basic usage
  • How to replicate componentDidMount with useEffect
  • How to replicate componentWillUnmount with useEffect
  • How to create self-sufficient code for componentDidUpdate with useEffect

This article will use React useState API in the examples.

So if you’re not familiar with how to use it, read a previous article that covers about it; Introduction to React hooks.

Come back when you’re ready!

What are hooks

Before I discuss about the useEffect API, it’s important to understand what a hook really means.

If you’re already and pro and know what a hook means, feel free to go to the next section.

Hooks is not a new concept, and has been implemented in tools such as WordPress and Drupal for many years.

A Hook is a function that allows you to execute some code, in a specific time of an applications lifecycle.

This holds true to React as well.

A React component has many phases that it goes through, even a functional component one.

And the React team has exposed those lifecycles for the engineer to execute some code for that specific lifecycle.

Some of the most common lifecycles that a React engineer is familiar with is componentDidMount, componentDidUpdate, and componentWillUnmount.

In a React class component you’d typically create a method and name it as shown above.

But in the new React hook API is a bit more implicit.

To access these lifecycle in a functional React component, useEffect was given to us.

What is React useEffect?

React useEffect is a function that gets executed for 3 different React component lifecycles.

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Whether your component is functional or a class type, it still has these 3 basic lifecycles.

So when you use useEffect in your functional component, and any of these 3 lifecycles that get pinged; your useEffect code logic will be executed (multiple times).

Basic usage of useEffect

React useState example

Here’s a small application, that prints out a message to the user.

React useState output example

But now I want to useEffect to change the message 1 second after the component did it’s first render.

React useEffect and useState example

I’ve imported useEffect from the React library and I’ve added my effect under the useState line.

useEffect accepts a function as it’s first argument, and inside that function I will add my effect.

The function acts as a callback function after one of the React component lifecycle has been triggered.

React useEffect output

It worked! But there’s a problem. Take a look at the console log. The effect got triggered twice.

This behavior is not optimal, because if you have multiple effects or have new prop values being tossed from a parent component, it may trigger the effect multiple times.

This may cause inconsistency, weird side-effects, or freezing your app from an infinite loop.

Let’s optimize this code a little bit more.

Replicating componentDidMount with useEffect

The goal now is to execute the setMessage function only on the componentDidMount lifecycle.

That way if the App component receives any new props or if the state changes, the effect won’t be triggered again.

Replicating React useEffect on componentDidMount

Can you see the difference from the old example?

If you haven’t caught it yet, I added an empty array bracket [] as a second argument to the useEffect hook function.

If you take a look at the console log it only shows “trigger use effect hook” once.

Here’s an example output with another console log message that says, “App component rendered,” after the effect function.

The second console message should only execute when the render lifecycle gets triggered.

If we take a look at the console log again, we can see the order of the lifecycles that it went through.

  1. Rendered lifecycle
  2. componentDidMount lifecycle
  3. Rendered lifecycle

This is the normal behavior that you would see in a traditional React class component.

By running an empty array [] as a second argument, you’re letting React know that your useEffect function doesn’t depend on any values from props or state.

This will help you avoid the componentDidUpdate lifecycle.

Accessing componentWillUnmount with React useEffect

I showed an example how to avoid a trigger from a componentDidUpdate lifecycle with useEffect.

But what if you have code that needs to get cleared up on a componentWillUnmount cycle?

How do we replicate a componentWillUnmount lifecycle?

P.S. this lifecycle is also known as, the cleanup in a React hook function.

In the next example I will demonstrate a use case where you’ll need to clean up your code when a component will unmount.

Body onResize event in React useEffect

In the image above, I have created a new function component called, WindowWidthSize.

The objective of this component is to print out the width size of the current window.

As you see can see, I’m using useState to keep track of the width size.

And right below it I’m adding a window event listener on the resize event.

So every time the user resizes the browser, it will get the new width, save it into state, and print out the new width size.

Show window width in React functional component

Okay, so here we have another React component that uses the component WindowWidthSize, and it has a magical button.

When a user clicks this magical button, the WindowWidthSize component will vanish before your eyes.

Great, the code works. And if you see on the bottom right there is a blue highlight section called Window.

The blue highlight shows the window event listener I have added.

When I click on the magical button, the WindowWidthSize component will no longer exist. But there’s a small problem.

The window event listener is still lingering around.

Situations like these are bad because this is what causes memory leaks in your app.

And you don’t want to be greedy with your customers limited bandwidth.

Let’s use the clean up technique that useEffect gives us, to get rid of this lingering window event.

Replicating componenWillUnmount with React useEffect. Remove event listener.

I added a return function inside the useEffect function.

And inside the return function I’m removing the even listener that I originally added.

When a functional component un-mounts the logic inside the return function will get executed.

So remember to clean up your code if necessary by returning a function inside the useEffect function.

Trigger useEffect only on componentDidUpdate

By default useEffect will trigger anytime an update happens to the React component.

This means if the component receives new props from it’s parent component or even when you change the state locally, the effect will run again.

In case you need the effect to trigger on a componentDidUpdate lifecycle, you want to try and make it as self-sufficient as possible.

If you don’t, you will run into an infinite loop of updates, and just run your computer hot.

Set counter in useEffect

Right above is a simple counter app. All it does is print a number to the user.

P.S. useState also accepts functions as an argument. This might be a better choice if you want a more accurate representation of the previous data.

I want this counter to increase on every second.

Increment counter every second in React useEffect
React counter output

This is good, but not good enough!

If you have multiple setStates inside this component or even receiving new props, this can throw the useEffect hook off track.

Causing your app to not be synchronized or just freeze.

Replicating componentDidUpdate with React useEffect

Now it’s more self-sufficient.

One, I’ve added a clean up function to clear the interval whenever the componet will unmount.

Two, I’ve added the counter variable inside the array bracket that’s found in the second argument of the useEffect function.

This tells React to only trigger the effect when counter is a different value.

If counter has not changed in value, the effect won’t execute.

This is helpful because you can safely add multiple useEffects, setStates, or even pass down new prop values, and it won’t desynchronize your counter component.

Conclusion

React useEffect is a basic hook that gets triggered on a combination of 3 React component lifecycles:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

If you’re planning to use React hooks you must know how to execute your effect on the right time.

Otherwise you might run into some problems for your users.