Abhinav Anshul
13 Dec 2021
•
5 min read
React Hooks were released a couple of years ago when React ecosystem introduced a new way to handle state and effects in a function-based component. Although there isn't any sudden plan to completely deprecate the class-based approach, however, web applications have been moving to hooks rapidly. In this article, we'll look into the
useLayoutEffect
hook and its similarities & differences with theuseEffect
hook.
Before we dive into useLayoutEffect
, it is worth mentioning that both this and the useEffect
hook is used to handle side-effects in React.
Another way of saying this would be when the DOM paints the screen, we want to do some activity
called side-effects that could include data fetching
, updating and changing DOM elements, subscribing to an event etc.
According to the official docs,
"The signature is identical to useEffect, but it fires synchronously after all DOM mutations."
As it has two parts, let's look at the first statement, "signature is identical to useEffect",
useEffect
and useLayoutEffect
shares similar function signatures, meaning the API exposed to the developers are similar in both cases.
Given below is a simple counter application using useEffect
that increments the count on a button click and prints the value using effect hook in the console.
function App(){
const [count, setCount] = React.useState('0')
React.useEffect(() => {
console.log(count)
}, [count])
return(
<div>
<h1>The count is {count} </h1>
<button onClick={() => setCount(count + 1)}></button
</div>
)
}
If we replace useEffect
hook with this instead,
React.useLayoutEffect(() => {
console.log(count)
}, [count])
The Counter app would still work the same, this establishes the fact about having identical function signatures as,
useLayoutEffect(() => {
// run effect here
}, [<dependency array>])
One thing to note here, it also returns either a cleanup function or an undefined value similar to the useEffect
hook.
Moving onto the next statement, "but it fires synchronously after all DOM mutations.", this is where useLayoutEffect
differentiates itself from useEffect
hook, by the order in which they are fired.
In the above useEffect
counter example, here's how React is rendering the function and handling interactivity:
a. When the App
component gets mounted to the screen, the default count
value 0
is printed along with the HTML button.
The count is 0.
b. The user clicks on the button and increments the count value by 1
.
c. React triggers the state value of the count mentioned in the useState hook, and re-renders the count value.
d. After the DOM is done with painting the screen with mutated value, useEffect
hook is triggered.
In a nutshell, a useEffect
hook only gets triggered once DOM is done painting the screen, making it asynchronous as it doesn't block the DOM manipulation on the screen.
However, things gets a little more interesting with useLayoutEffect
. It gets triggered synchronously. In other words, it doesn't care if the DOM has painted the screen.
We'll deep dive into its working below:
As we have seen, useLayoutEffect
doesn't wait for DOM to paint the screen and gets fired right away. This sure affects the order of execution.
In a useEffect hook,
useEffect(() => {
console.log("log 1")
}, [])
useEffect(() => {
console.log("log 2")
}, [])
The code prints:
log 1
log 2
This is an expected behaviour while working with useEffect
. It executes in the order in which they were mentioned.
Now, let's replace the second hook with a useLayoutEffect
:
useEffect(() => {
console.log("log 1")
}, [])
useLayoutEffect(() => {
console.log("log 2")
}, [])
The above prints,
log 2
log 1
As expected, the useLayoutEffect
doesn't care for DOM mutation hence gets executed even before the useEffect
hook.
The official React doc suggests using the useEffect
hook as much as possible and there is a 99% per cent chance it will suit most of the use cases in any application.
As useLayoutEffect
is synchronous, React waits for it to finish then updates the screen.
As of rule of thumb, you can think of it as an App
component that would wait to get visually updated until the useLayoutEffect
has been finished running.
This pauses the React component for a split second, as it is "waiting" for something to be finished, this behaviour might cause a performance hiccup.
Hence performance-wise, useLayoutEffect
can be expensive to run.
In complex user interaction which involves animations, it is possibly better to use useLayoutEffect
instead of useEffect
especially if you're dealing with ref
React.useLayoutEffect(() => {
console.log(ref.current)
})
In the above, contrived example, useLayoutEffect
ensures to wait, updates its value then move on to the other piece of code. This might improve on the animation flickering which usually happens with the useEffect
hook.
This is a tradeoff between running expensive hook v/s having a smooth animation. However, React is fast enough to optimize on small use cases and we won't be bothered between the two at all.
There's an infamous useLayoutEffect
warning,
"Warning: useLayoutEffect does nothing on the server because its effect cannot be encoded into the server renderer's output format..."
When dealing with SSR, both useEffect & useLayoutEffect won't work unless that JavaScript has been properly loaded. Therefore, there's the above warning we might see in the console. The reason why it doesn't generate with useEffect
is, it is not concerned with the render cycle of the component unlike useLayoutEffect
is concerned and cares what users would see on the very first render of the component.
React Community suggests two ways to fix this,
1. Of course, the first thing we can do is to try to convert it to a useEffect
hook if possible.
2. If there is a flickering issue with the useEffect
(as we discussed earlier ) and developers do need useLayoutEffect
, then another way could be delaying that very component that uses the hook until the JavaScript has been loaded. Another way of saying, lazily load the React Component.
In this post, we read how similar useEffect
& useLayoutEffect
can appear to be. However, the order in which both these hooks gets called is completely different. In small scale applications, both might look interchangeable but the latter shines while dealing with complex user animations, handling refs
in React, dealing with Layout on a page etc. Although useLayoutEffect
can be a little expensive to deal with compared to the useEffect
hook, there are always tradeoffs while building applications. Finally, we learnt about how to use it with an SSR based application.
Some Important Resources that I have collected over time:
Loved this post? Have a suggestion or just want to say hi? Reach out to me on Twitter
Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ
108 E 16th Street, New York, NY 10003
Join over 111,000 others and get access to exclusive content, job opportunities and more!