bvaughn/react-virtualized-auto-sizer

Autosizer sets state in componentDidMount causing a nested update

dinkinflickaa opened this issue · 3 comments

Autosizer sets state in componentDidMount phase which will block browser from repainting. This can be a big performance bottleneck.

Here is a codesandbox to demonstrate how changing state in componentDidMount phase can take longer to paint

https://codesandbox.io/p/sandbox/react-class-forked-5ffvgl?file=%2Fsrc%2Findex.js

Trace with state change inside componentDidMount
image

Trace without state change inside componentDidMount
image

Has anyone else run into this? Can we get the state changes before the first rather and avoid a nested update?

Layout effects are permitted when a component should re-size its contents before the browser paints. (This is done to avoid flickering/shifting.)

The main problem in the above screenshot is either (a) something in your code is triggering repeated renders+updates or (b) one or more of your components is doing too much work in render to be able to update quickly. Hard to tell which.

Layout effects are permitted when a component should re-size its contents before the browser paints.

@bvaughn
In this case aren't we triggering resize manually though? See: https://github.com/bvaughn/react-virtualized-auto-sizer/blob/master/src/AutoSizer.ts#L65C1-L66C1

The main problem in the above screenshot is either (a) something in your code is triggering repeated renders+updates or (b) one or more of your components is doing too much work in render to be able to update quickly

The screenshot is taken from codesanbox link's browser trace. https://codesandbox.io/p/sandbox/react-class-forked-5ffvgl?file=%2Fsrc%2Findex.js

All it does is:

  1. On Page load renders 10k components. Nothing virtualized.
  2. Each component implements a useEffect mount and cleanup.
  3. On click, Re-creates the class component which changes a text property in componentDidMount

So there aren't repeated re-renders but paint is delayed here because it has a lot of slow side effects which have to be flushed synchronously because of the nested update. This can be quite terrible when you are unmounting a lot of components before mounting AutoSizer

I'm not sure what to say, except...don't re-render 10k components like that 😄 It's never going to be performant. Switching from a layout effect to a passive effect will not make a difference in many cases, (often something else will trigger a sync update which will cause React to also flush passive effects before proceeding). And even if that's not the case, it's still going to take a ton of CPU time.

Layout effects are for layout (resizing and positioning) which is exactly what this component does.