Introduction

React has, not so long ago, come with a major update to its Functional Components (In v16.8, back in March of 2019), which has finally provided those components with a way to become stateful.

The addition of Hooks not only meant that Functional Components would be able to provide their own state but also manage their own lifecycle events through the introduction of the useEffect hook.

Moreover, the update has also introduced a brand-new, useLayoutEffect hook, which, according to the React documentation, does a pretty similar thing to what the useEffect hook does; so this begs the question: what’s really the difference between the two?

If you aren’t already familiar with hooks, I do recommend checking out this article where you’ll get a good understanding of what they are, what they are used for, and how you can use them in your applications.

In this article, we will be looking in more depth at what does each hook do, what are the differences between the two, and, towards the end, you should be having a better understanding of how each of them works, when you should be using one over the other, and some common pitfalls of falling in love with either.

Overview of the useEffect hook

This hook has been initially added as an alternative way of handling side effects in Functional Components, similarly to the componentDidMount and componentDidUpdate methods in Class-based Components (They also run at the same time in comparison).

Thus, its equivalent in Class-based Components would be a combination of the componentDidMount, componentDidUpdate, and componentWillUnmount methods, the latter being an effect of the return statement within the callback passed as an argument to the hook.

Hook Flow

  1. You cause a render somehow (the state has been updated or the parent re-renders)
  2. React renders your component (calls it)
  3. The screen is visually updated
  4. Then useEffect runs

Overview of the useLayoutEffect hook

Similarly to useEffect, it runs at the same time that componentDidMount and componentDidUpdate do in Class Components. However, useLayoutEffect runs synchronously, as opposed to useEffect, which means that the callback that it receives will only be invoked after the V-DOM computations have taken place in the component, but before they have been painted to the actual DOM.

That means that in case we do need to make any JavaScript queries for any DOM elements, useLayoutEffect is the hook we might need.

Hook Flow:

  1. You cause a render somehow (change state, or the parent re-renders)
  2. React renders your component (calls it)
  3. useLayoutEffect runs, and React waits for it to finish.
  4. The screen is visually updated

When to use the useLayoutEffect hook?

There is a common issue when using the useEffect hook where a component might “flicker” when its state is updated. That’s because the component will first render in a partially-ready state as it’s continuing the async computations process, and only then will it re-render in its final state.

That’s a good indication that you might want to use the useLayoutEffect hook instead.

Such an example would be trying to generate and render an extremely high numeric value at the click of a button like in this example:

import { useState, useEffect } from 'react'; import './App.css'; function App() { const [value, setValue] = useState(0); useEffect(() => { if (value === 0) { setValue(10 + Math.random() * 200); } }, [value]); console.log('render', value); return ( <div className="App"> <p>Value: {value}</p> <button onClick={() => setValue(0)}> Generate Random Value </button> </div> ); } export default App;

If we were to check how our application behaves, we shall see a changing flickering number each time we were to press the button:

Flickering Component On State Update

If we were to use the useLayoutEffect hook instead, we would be able to get rid of this visual bug:

import { useState, useLayoutEffect } from 'react'; import './App.css'; function App() { const [value, setValue] = useState(0); useLayoutEffect(() => { if (value === 0) { setValue(10 + Math.random() * 200); } }, [value]); console.log('render', value); return ( <div className="App"> <p>Value: {value}</p> <button onClick={() => setValue(0)}> Generate Random Value </button> </div> ); } export default App;

And, as you can see from the last video sample, we have been able to get rid of the flickering effect.

That’s because of the slight difference in behavior between the two hooks; while the first handles the computation and DOM rendering asynchronously, the latter does the computation first, and only then handles the rendering of the result of that computation to the screen.

In our case, that means that the useEffect hook is trying to generate the random value and also render it to the screen at the same time. On the other hand, the useLayoutEffect hook is trying to finish the computation first, and only then present us with the generated number.

Summary

As you’ve noticed, the useLayoutEffect hook provided us with a clean solution to a frequent issue we might find ourselves fighting with oftentimes, and that would be getting entangled in React’s way of handling DOM painting and computations simultaneously.

As a takeaway, you might want to use the useLayoutEffect hook whenever you are working with ref values or need to do any type of work revolving around querying for DOM elements through vanilla JavaScript methods such as querySelector, querySelectorAll, or any other way of doing that.

Keep in mind, though, that despite the usefulness that the useLayoutEffect hook provides us with, in 99% of cases you would still be better off using the useEffect hook, as it is oftentimes much more performant due to its async nature.

So, that was it.

Hopefully, you’ve now got a better understanding of the usage of both hooks, as well as the scenarios where each might come in handy; that’s also especially common since it is rather underused either due to developers not knowing about it, or knowing about it, but being unsure about what it does exactly.

In case you feel I’ve missed something, or you would like to debate any aspect further, or even leave any feedback for this article, feel free to leave a comment below.

See you on the next one. Cheers!

👋 Hey, I'm Vlad Mihet
I'm Vlad Mihet, a blogger & Full-Stack Engineer who loves teaching others and helping small businesses develop and improve their technical solutions & digital presence.

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.