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 and this can be useful for many different reasons. The useLayoutEffect() hook is a hook you have probably never heard of and if you have heard of it, then you might not know when exactly to use it as this hook and the useEffect() hook are nearly identical.
useLayoutEffect() is different from useEffect() in that it is not asynchronous.
In this article, we’ll simplify the use of React’s useLayoutEffect() and explain how to position them to maximize performance and speed up problem-solving.
Prerequisites:
Readers should be conversant with: Vanilla JavaScript, React JS, CSS.
Development Area
npx create react app
cd app
//clear the App.js component and replace with anything
import React from 'react';
import './App.css';
function App() {
return (
<div className="">
<h1>useLayoutEffect vs useEffect</h1>
</div>
);
}
export default App;
Our DOM should resemble the image below:
What Does the useLayoutEffect() Hook Do?
Let’s take a counter, for example, we will have a basic counter application that decrements by 2. This is what the code would look like:
import React, {useState, useEffect} from 'react';
import './App.css';
function App() {
const [count, setCount]=useState(0);
useEffect(()=>{
console.log(count)
}, [count])
const counter=()=>{
setCount(count=> count-2);
}
return (
<div className="container">
<button onClick={counter}>Decrement</button>
</div>
);
}
export default App;
With a very, very basic style:
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
UseLayoutEffect() is mostly similar to useEffect on how you define them, if you are unfamiliar with useEffect() you can learn more about it here. The above code has our state to be 0, and we tell our useEffect to console.log count’s present value, we went ahead to create our decrement function and passed it to our JSX so we can see it rendered on our browser.
With useEffect what we do is, pass in a function and then an array of dependencies, and every single time one of the dependencies in that array changes, it’s going to run the code in our function.
Important Things To Note About useEffect()
- It is asynchronous, so React is going to calculate all your DOM changes.
- It’s going to render your DOM out to the screen and then at some point the useEffect is going to run in the background but it is not going to block the DOM, it happens asynchronously in the background and it will log out whatever it can, usually, it is always quick.
- It adds a button’s event listener.
- It obtains data from the API while mounting a component.
The Difference Between useLayoutEffect and useEffect
The major difference between the two is that the LayoutEffect() is not asynchronous. Let’s go back to our code and modify it with LayoutEffect(). First, we import it and then replace useEffect with it.
import React, {useState, useEffect, useLayoutEffect} from 'react';
import './App.css';
function App() {
const [count, setCount]=useState(0);
useLayoutEffect(()=>{
console.log(count)
}, [count])
const counter=()=>{
setCount(count=> count-2);
}
return (
<div className="container">
<button onClick={counter}>Decrement</button>
<br />
<div>{count}</div>
</div>
);
}
export default App;
The above code is almost an exact replica of the one before, with a difference in the utilization of the useLayoutEffect() hook.
We can see that the output is exactly the same, the only difference is when useLayoutEffect() runs. UseLayoutEffect() runs synchronously between when React calculates your DOM and when it actually paints it out on the screen.
React is going to calculate the position of everything in the DOM, it’s going to pass that to the browser, and then when the browser paints the information to the screen all of our useLayoutEffects are going to run which means useLayoutEffects are perfect when you need to do something based on the layout of the DOM, that is where LayoutEffect comes from.
If you need to measure DOM elements, if you need to move things to the DOM that are going to be visible to the user, you need to use useLayoutEffect(). useLayoutEffect is not quite as performant since it happens synchronously instead of asynchronously which is why you should always try to use useEffect() first and then only if doesn’t work, then you can go ahead and implement UseLayoutEffect().
This may be a little bit confusing when you want to use one over another, so we have put up an example to explain this much better with the code below:
useEffect(()=>{
console.log("useEffect here!")
})
useLayoutEffect(()=>{
console.log("useLayoutEffect here!")
})
Result:
As we can see, despite useLayout being placed after useEffect() it still manages to run before useEffect(), they are very similar but they have a few minor differences. useLayoutEffect runs before useEffect and also runs before the DOM is printed out on the screen, so React calculates the DOM measurement, then runs useLayoutEffect, prints the DOM on the screen, and then runs useEffect. useLayoutEffect is only useful when you need to do something before the DOM gets printed out on the screen, below we have a perfect example of where useLayoutEffect makes sense:
useEffect
import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';
import './App.css';
function App() {
const [message, setMessage] = useState("hello ninjas");
useEffect(() => {
setMessage('welcome to common ninja blog')
}, [message])
return (
<div className="container">
<br />
<h4>{message}</h4>
</div>
);
}
export default App;
Result:
Code-Explanation:
In the code above we imagine having text that is updated using the useEffect() hook, and passed to the JSX, we noticed whenever we reload this page, we get the initial text and in a flash, it changes to the updated one, we do not want that to happen, the immediate solution is to fix it with useLayoutEffect().
Replaced with useEffectLayout:
import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';
import './App.css';
function App() {
const [message, setMessage] = useState("hello ninjas");
useLayoutEffect(() => {
setMessage('welcome to common ninja blog')
}, [message])
return (
<div className="container">
<br />
<h4>{message}</h4>
</div>
);
}
export default App;
Result:
We can notice that there is a change and below is why.
Why Use useLayoutEffect()?
So why would we want to use useLayoutEffect() if doing so will cause a component’s painting to be delayed? When running code that directly alters the DOM in a way that the user can observe, useLayoutEffect is most frequently used. useLayoutEffect is the ideal option because we are directly altering the DOM and the user can see the changes, like in the case of changing a DOM element’s background color as a side effect.
If we were to employ useEffect, we might have a problem where the DOM gets painted ahead of the execution of the useEffect function. Due to the useEffect code, this would result in the DOM element initially being the incorrect color before changing to the correct color, but with useLayoutEffect() being able to run before the DOM there is a behind-the-scenes automated Effect.
Conclusion
Although useLayoutEffect is a very helpful hook in some circumstances, useEffect will work just fine in most circumstances. Additionally, if useEffect functions well, it is preferable to use it because it does not prevent painting.