Avoid flickering with useLayoutEffect

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.

image

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.

How does it work?

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.

idle callback mechanics

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.

References

  1. useLayoutEffect - React Documentation
  2. Using requestIdleCallback