Back

UseEffect vs UseLayoutEffect in React

UseEffect vs UseLayoutEffect in React

Initially, when a component first loads and the state changes, it re-renders the DOM so we can update that state in the browser. The UseEffect hook is a way to run code on every render, which can be useful for many reasons. The UseLayoutEffect hook is a hook you have probably never heard of, and if you have heard of it, you may not know when exactly to use it: this hook and the UseEffect hook are nearly identical. UseLayoutEffect differs from useEffect() because it is not asynchronous.

Goals

This article will identify areas where useLayoutEffect() would be better than UseEffect(). We’ll simplify the use of React’s useLayoutEffect() and explain how to position it to maximize performance and speed up problem-solving. In the course of this tutorial, we will have to build a simple application that resembles the one below, built with the useEffect() hook:

-

We will also recreate the above, using the useLayoutEffect() hook instead:

-

The differences observed in both apps built with useEffect() and useLayoutEffect() respectively will be the core reasons for this article.

Utilizing UseLayoutEffect()

The UseEffect() and useLayoutEffect() hooks are mostly identical, and their syntax is the same as seen below;

//useEffect()
useEffect(() => {
  setMessage('welcome to open replay blog')
}, [])

//useLayoutEfect
useLayoutEffect(() => {
  setMessage('welcome to open replay blog')
}, []) 

Key differences between useLayoutEffect() and useEffect()

So, with their identical syntaxes, what is the difference between these two hooks? Well, there are three key differences: The asynchronous nature of useEffect The DOM flashes The fact that useLayoutEffect is executed before useEffect.

UseEffect() is asynchronous by nature: it will not delay painting the DOM to the browser, but on the other hand, the useLayoutEffect() hook is synchronous, and it will delay painting the DOM to the browser. It runs its code before painting to the browser. So when does this come in handy? Well, it makes the useEffect() hook the correct and more performant choice about 95% of the time.

However, just that 5% percent of the time useLayoutEffect() has, it takes full advantage of its potential. When you want to choose useLayoutEffect over useEffect and that is because you will want the code to run before it directly modifies the DOM and that modification, of course, is observable to the user, you don’t want the user to observe that change, and that brings us back to the example that I gave at the beginning of this tutorial.

If you notice, whenever I click the button, the number changes, and the whole section element tends to move, but this is observable to the user. What I do want to do, as seen in the second GIF in the application demo, is have the element move but without displaying the change in number until it has moved.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

A short demo

Here is the code for the demo;

import { useState, useRef, useEffect, useLayoutEffect } from 'react';

function App() {
  const [count, setCount] = useState(0);
  const [elementStyle, setElementStyle] = useState({});
  const elementRef = useRef();

  useEffect(() => {
    const atRandom = Math.floor(Math.random()*500)-50;

    for (let i = 0; i <= 100000000; i++) {
      if (i === 100000000) setElementStyle({ paddingTop: `${atRandom}px` })
    }
  }, [count])
/*the loop above just serves to make the changes in this example visible by slowing them down */

  return (
    <div className="app">
      <div ref={elementRef} style={elementStyle}>
        <p>{count}</p>
        <div>
          <button onClick={() => setCount(prev => prev + 6)}>+</button>
        </div>
      </div>
    </div>
  ); 
}
export default App;

We should be able to have the GIF below after basic styling

.btn{
    background-color: rgb(250, 197, 119);
    border: none;
    color: white;
    padding: 15px 32px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;    
}
.app{
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center; 
}
.p{
  text-align: center; 
  text-decoration: none;
  font-size: 16px;    
}

-

We need to modify the code with useLayoutEffect(); this is what it looks like:

useLayoutEffect(() => {
  const atRandom = Math.floor(Math.random()*500)-50;

  for (let i = 0; i <= 1000000; i++) {
    if (i === 1000000) setElementStyle({ paddingTop: `${atRandom}px` })
  }
}, [count])

You can see the difference: -

DOM Flashes, another key difference to note here that comes in handy is if you see a flash effect on the screen numerous times, you are changing several things in the DOM that take a while to process. Therefore, you may see that flash when the repaint happens, like the code below prints:

import React, { useState, useEffect, useLayoutEffect } from 'react';
import './App.css';
function App() {
  const [message, setMessage] = useState("hello dev's");

  useEffect(() => {
   setMessage('welcome to open replay blog')
  }, [])
  return (
    <div className="container">
      <h2>{message}</h2>
    </div>
  );
}
export default App;

Replacing the code below with useLayoutEffect() returns a different result that solves the flashes. UseLayouEffect() runs before useEffect(): this last key difference is minor but still important. Similar to the first difference indicated, useLayouEffect() runs before useEffect().

import React, { useState, useEffect, useLayoutEffect } from 'react'
import './App.css'
function App() {
  const [message, setMessage] = useState("hello dev's")
  useEffect(() => {
    setMessage('welcome to open replay blog')
    console.log(message)
  }, []) 

  useLayoutEffect(() => {
    setMessage('welcome to open replay blog')
    console.log(message)
  }, [])

Image: -

As we can see above, the useLayouteffect code always comes before useEffect.

Conclusion

Generally speaking, it is best to always use useEffect and only switch to useLayoutEffect if useEffect genuinely creates flickering in your DOM or produces an inaccurate result. Although useLayoutEffect is a very helpful hook in some circumstances, useEffect will work just fine most of the time. Additionally, if useEffect functions well, it is preferable to use it because it does not prevent painting.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay