App: [Performance] [Audit] `ThumbnailImage` triggers layout shifts, impeding FlatList performance

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


This report is part of #3957, scenario “Rendering Individual chat messages”.

Flamegraph

The full commit log can be inspected with Flipper or React Devtools, see https://github.com/Expensify/Expensify.cash/issues/3957#issuecomment-881045715 for instructions.

You can witness the layout shift behavior in the below Framegraph (c244, select VirtualizedList component) image

The four commits (c222, c244, c266 and c280) are caused by last state of VirtualizedList component being changed, this is the signature of layout shifts. There are exactly 3 images in the report. So we have 1 initial commit + 3 layout shifts. Those 3 layout shifts span over 1.3s, this is considerable.

To get more into the details of VirtualizedList internals, a layout shift causes _onCellLayout callback to be invoked, which calls _scheduleCellsToRenderUpdate which calls _updateCellsToRender, which in turn causes last state to change.

Cause

https://github.com/Expensify/Expensify.cash/blob/76c3157a41d86beb89c8cf107a68573280c06ce7/src/components/ThumbnailImage.js#L59-L63

The ThumbnailImage component first draws a 100x100 box to display the image, then waits for physical dimensions to be available and redraws the image with the newfound dimensions. This causes the FlatList cell layout to shift, and requires FlatList to recompute the position of each cell. This can have a sensible impact on performance if a discussion has many image attachments, since every shift will cause the FlatList to recompute the position of each cell, potentially re-rendering some cells.

Proposal: store images physical dimensions in markup

Store the physical dimensions of the image as attributes (width, height) of the <img> element. Thus, we can render the image one and only once and avoid those costly shifts.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 38 (25 by maintainers)

Most upvoted comments

Agreed, @kidroca. Going to close this since we’ve taken differing approaches to speeding up image handling.

Still no internal takers on https://github.com/Expensify/Expensify/issues/173677. I may pick it up before week’s end if nobody beats me to it.

@thienlnam add me back if we need this added to Upwork 😃

Just want to state that I have seen no evidence that react-native-fast-image will provide any huge performance boosts for this app. I’ve set it up a few times and there was not really any noticeable changes. But it takes 5 minutes to set up if someone can create a benchmark to prove the value.