question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[VirtualList] Render blockages for multiple seconds

See original GitHub issue

Is this a bug report?

Yes.

Have you read the Contributing Guidelines?

Yes.

Environment

Environment: OS: macOS Sierra 10.12.6 Node: 6.10.2 Yarn: 0.15.1 npm: 4.6.1 Watchman: 4.1.0 Xcode: Xcode 9.1 Build version 9B55 Android Studio: 2.3 AI-162.4069837

Packages: (wanted => installed) react: ^16.0.0 => 16.0.0 react-native: file:…/…/react-native => 0.50.2

Steps to Reproduce

When you use a VirtualList with refresh component, and then hide the refresh component to show a list of variable sized elements (ie, a section list with headers that are differently-sized than the elements), you can have extraordinarily-long delays during the second rendering frame.

Expected Behavior

It renders quickly.

Reproducible Demo

You can see a repro case here: https://snack.expo.io/HkRgOcEZG

After the simulated 500ms load, it proceeds to render item 0. Then it gets “stuck” for a 5+ seconds before rendering items 1-to-N. You can make it even worse by uncommenting maxToRenderPerBatch={1} (ie, rendering in smaller batches makes it even slower, and makes it take even longer to render an initial batch of items!)

===============

My long-breakdown of what’s going on from poking around with debug statements:

When the list loads and the refresh indicator hides, in _scheduleCellsToRenderUpdate the distTop is negative, and the velocity is negative (due to the refresh indicator being hidden). Because it’s negative, it triggers a “hi priority” updates whenever _scheduleCellsToRenderUpdate is called. Even when there are more than enough elements on screen. This is Bug 1 (ie, logically, the user is not scrolling, so I’m not sure this should be a high priority update).

After the first cell is laid out in _onCellLayout (ie, the first initialNumToRender cells) and it has an average cell size , it calls _onCellLayout->_scheduleCellsToRenderUpdate->_updateCellsToRender, and uses computeWindowedRenderLimits to compute the new state of the {first, last} render window. And computeWindowedRenderLimits, uses the average cell size to figure out how many cells are visible (and how many should be rendered for the overscan), and computes the new {first, last}.

This setState with new state triggers a call to componentDidUpdate, which due to the potential layout of new cells in the {first, last} window, triggers another call to _scheduleCellsToRenderUpdate.

This runs through the whole loop shebang again, computing another high priority update of computeWindowedRenderLimits…but this time the average cell size has changed (due to some new cells, I think? Not entirely sure…). This causes it to generate a different {first, last} (sometimes larger, sometimes smaller), which then triggers the whole shebang all over again when componentDidUpdate gets called.

And unfortunately, after the first render…the system proceeds to run through this componentDidUpdate->...->setState->componentDidUpdate loop many many times, without having time to actually push the render state to the screen. And each time through, the schedule call sees a high-priority update due to the negative distTop (due to the refresh control), so it can’t relax.

This loop can run many times before it “settles down” on an final render window, leading to a visible lag in rendering all these items. In fact, smaller maxToRenderPerBatch will make this take even longer.

During all of this cycling, two things are noticeable in the UI:

  • my Touchable elements don’t actually respond, and I can’t actually click on the elements yet.
  • The initial render is complete (with initialNumToRender), but the second batch never gets a chance to render to screen. This causes my List to be stuck with an incomplete list for multiple seconds, before the internal loop finally “settles”, gives up on state-changes and high-priority updates, and lets the system render the rest of my items properly.

Unfortunately, I’m not sure of a correct fix here. Workarounds include:

  • Passing a pixel-perfect getItemLayout, though this is super-tricky to get right with SectionLists (and I failed last time I tried).
  • Disabling virtualization, since there is no longer a ‘render window’ to constantly update (and fail to update accurately), though this causes my lower-end clients to OOM.

Other suggestions/fixes would be appreciated…

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:1
  • Comments:16 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
boliveiracommented, Apr 23, 2018

Hi, sorry to bother you guys, but I am having a kinda similar problem and I believe that computeWindowedRenderLimits in VirtualizeUtils.js has a mistake.

I believe the line that is wrong is this one:

const overscanLength = (windowSize - 1) * visibleLength;

VisibleLength is the visible area of the virtualizedlist. It will give a very big overscanLength that will render much more cells than the specified in the windowSize.

So if we replace that line for this one:

const overscanLength = (windowSize - 1) * getFrameMetricsApprox(0).length;

Then the windowSize parameter is respected. Maybe this is the problem that you guys are experiencing?

1reaction
boliveiracommented, Apr 26, 2018

@jochem725 , probably it will take a bit more time on scrolling because it’s now respecting the windowSize, which means the number of cells already rendered before a scroll is less than before. You can try playing with windowSize parameter to find a nice compromise between render/scroll performance.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Testing a Virtual List component with Cypress and Storybook
Components are the building blocks of your app, Storybook allows you ... This test is enough to check that the Virtual List renders...
Read more >
Rendering large lists with React Virtualized - YouTube
Learn how to use react-virtualized to display a large amount of data efficiently.Introduction -- 00:00Getting started/creating example data ...
Read more >
Efficient rendering of really large lists with - fasterthanlight.me
Rendering long select lists in react can be very heavy and inefficient. The article shows how to use a common virtualization technique to ......
Read more >
Front-end multi-data rendering optimization - 前端指北指南
The page was refreshed and scrolled within 10 seconds, and it can be seen that the rendering of the dom blocked the page...
Read more >
CS50's Mobile App Development with React Native 2018
0:20:14It says, OK, we need to add something to our virtual list ... 0:39:10And if we click this, we see it takes multiple...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found