useLayoutEffect
is one of the recent additions to React. Just like useEffect
it lets you perform side effects like network calls, subscribing to events, or even changing the DOM manually. React fires both of them after performing the DOM updates.
It’s basically the same as useEffect
except it fires before the browser repaints the screen.
As you can see on the image we’re avoiding unnecessary repaint with useLayoutEffect
. It happens because browser cannot paint updates until the effect runs. And btw it runs syncronously if it was not clear.
It’s useful in the situations where we want to run the side effect and update the DOM before showing our users the updates.
One use case could be scrolling to the particular area on the page after navigating. Without useLayoutEffect
we’d see the page from the top and then scroll to the area. So it would be noticable for our users.
You’ve probably heard before about requestAnimationFrame
. In short it tells the browser to run the callback before next repaint and that’s exactly what we need for our effect.
JavaScript is single-threaded. It means that a single thread is responsible for unning code, executing document layout and paint. It also means that if you’ll run a long running syncronous task it will cause frame drops. It’s important to make sure that JavaScript runs tasks small enough so they can be completed within a frame. requestAnimationFrame
on the other hand allows you to divide JavaScript operation into small chunks and schedule it to run at every frame. If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed. It means that style changes will have been applied and layout calculated. If we make DOM changes inside of the idle callback, those layout calculations will be invalidated.
The best practice is to only make DOM changes inside of a requestAnimationFrame
callback, since it is scheduled by the browser with that type of work in mind.
Adopting requestAnimationFrame
allows us to schedule animations properly and maximize our chances of hitting 60fps. One caveat though is that it’s impossible to figure out exactly how much frame time remains because after requestAnimationFrame
callbacks execute there are style calculations, layout, paint, and other browser internals that need to run. This information is available to browser though. And a new method requestIdleCallback
has been introduced recently to give us access to this data. It’s designed to schedule work when there is free time at the end of a frame, or when the user is inactive. You can use this method to schedule non-essential work like sending analytics events. Depending on your use case you can also leverage the method to modify DOM.
From the chart we can see that any changes scheduled in the end of the frame will be applied after current frame has been commited. It means that any logic inside requestIdleCallback
wouldn’t interfere with style changes and layout calculations needed for current frame to finish render.
On the other hand if we manipulate the DOM inside requestIdleCallback
the previous layout calculations intended for the current frame will be invalidated. In most of the situations you should manipulate the DOM in requestAnimationFrame
.