Masonry: Scroll throttle + memo component (MasonryV2) #3533
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR applies two more optimizations to MasonryV2: scroll throttle and memoizing each masonry item that we render
What changed?
Scroll Throttle
I realized that one change I did not carry over from current Masonry is throttling the callback of the scroll event listener, which means that we're currently triggering a render every time scroll position changes (which is a lot). This PR reintroduces the same throttle that was currently use in control Masonry.
MasonryItem + Memo
I was doing some investigation into React's startTransition API and noticed that it actually causes React to render more. A github thread I came across which was particularly interesting was : facebook/react#24269. Based on this, one recommendation to reduce the impact from rendering more was to memoize the component that is expected to change (as a result of the transition).
In Masonry's case, this would be the actual grid items that we render so I moved this out into a separate
MasonryItem
component and wrapped it in a React.memo.This is effectively the functional equivalent of React's
PureComponent
class where React will skip re-rendering if props have not changed.Testing
I did quite a bit of testing and the results are promising. To simulate rendering a more expensive grid item, I updated
ExampleGridItem
to recursively render a deeply nested tree.Initial Load
The main impact to initial load is that in control, all renders take about the same time whereas in V2, the render time falls off significantly after the first two renders.
V1
V2
Scrolling
This is where the impact really shows. During scroll, we can see that in both V1 and V2 (without changes), every grid item is repeatedly rendered. With the changes in this PR, however, only a small subset of grid items are rendered which reduces render item significantly (50ms per render to ~5ms)
V1
V2 (without change)
V2 (with change)
Additionally, we also see significantly less long tasks and scroll jank with this change
V1
V2 (without change)
V2 (with change)