How To Improve Performance Using useLayoutEffect In React Sites?

Akash Shyam
Frontend Weekly
Published in
3 min readAug 10, 2021

Most of us just use useEffect for everything(API calls, DOM manipulations using useRef etc). 95% of the time, the useEffect is the right choice. Both of them are very similar, but have different use cases. Let’s look at what they are and then we’ll do a compare.

useEffect

Most of you know what this does, but we’ll still do a quick overview to refresh your memory. This hooks lets perform side effects in functional components. It takes in a function that runs some code in the background after the component is mounted and an array of dependencies(when these change, the function will rerun).

useEffect(() => {
// Execute some code in background
// Cleanup function
return () => {};
}, []);

useLayoutEffect

This hook is executed almost the same as the useEffect. It takes a function which runs the side effect. The return value of the function(which is a function or undefined) does cleanup. Here’s a useful metaphor to understand the system of effects and cleanup functions:

Let’s say you have booked a hotel in another city for the next week. But, due to a family emergency, you have to cancel. So, we need to inform the hotel that you can’t make it and your visit should be postponed/cancel.

In a similar fashion, the side effect is booking a hotel. The “family emergency” in this case is the component being unmounted(the cleanup function is called when this happens). Because we can’t make it, we’ll need to inform the hotel that we need to cancel which is basically the clean up function. Here’s how we can use this hook:

useLayoutEffect(() => {
// DOM manipulation
// Optional cleanup function
return () => {};
}, []);

The Grand Comparison

  • useEffect

The catch is that useEffect runs asynchronously and after a render is painted to the screen. It's more performant this way and perfect for API calls, most of the time this is what you want.

However, if your effect is mutating the DOM(DOM node ref), then the user will see a flicker of content. For a split second, the page will be blank and then it will be visible again in a second. This is due to the time taken for the useEffect to mutate the DOM and then render it. Here’s a step by step process of what happens:

  1. A render is triggered in the application(dependency change)
  2. React renders your component
  3. React updates the screen(visually)
  4. Then, the useEffect is called in the background.
  5. If you mutate DOM node refs in useEffect, then a re-render is triggered, and while the effect is changing the DOM, the screen goes blank. Once the effect is done mutating the DOM, the screen is updated
  6. Rinse and Repeat!
  • useLayoutEffect
  • useLayoutEffect

On the other hand, this runs synchronously after the component is updated. Here’s how this works:

  1. A render is triggered(dependency change)
  2. React renders your component
  3. Next, useLayoutEffect is called
  4. As I said, useLayoutEffect is synchronous so React waits for it to finish
  5. The screen is updated
  6. Repeat again!

A simple rule of thumb I follow: just try using useEffect 99% of the time; if your component is flickering, try switching to useLayoutEffect.

The reasoning behind this rule is because useLayoutEffect is synchronous which means the app won’t visually update until your effect finishes running… it could cause performance issues like stuttering if you have slow code in your effect. Along with the fact that most effects don’t need the world to pause while they run, regular useEffect is almost always the one to use.

That’s all for now, I hope you guys liked this post! Follow me on twitter where I daily post tips, tricks and memes for developers. Bye for now 🤘

--

--

Akash Shyam
Frontend Weekly

Full-stack aficionado | Enhancing SaaS & indie maker products and helping them excel 🔥