React-native: SectionList (and probably VirtualizedList/FlatList) fails to batch row renders on re-renders

Created on 19 Sep 2017  Â·  14Comments  Â·  Source: facebook/react-native

Is this a bug report?

Yes

Have you read the Contributing Guidelines?

Yes

Environment

  1. react-native -v:
    react-native-cli: 2.0.1
    react-native: 0.48.3
  2. node -v: v8.5.0
  3. npm -v: 5.3.0
  4. yarn --version: 1.0.2

Then, specify:

  • Target Platform: iOS, Android

  • Development Operating System: macOS 10.12.6

  • Build tools: n/a

Steps to Reproduce

  1. Create a SectionList (I expect it's the same for FlatList and VirtualizedList too) with a large number of items – several hundred.
  2. After it has rendered, trigger a re-render of the SectionList by changing its data. I do this by applying a filter to the data.

Expected Behavior

The initial render of the SectionList works well for me. My understanding is: when SectionList is first rendered, it renders a minimal number of items (according to initialNumToRender) and then releases the JS thread to allow user interaction with the page. It then continues to render off-screen rows in batches. Every second or so it will render a batch (of size determined by maxToRenderPerBatch) and then release the JS thread again. It continues this loop until it has filled the window large enough to satisfy the windowSize prop. In my application, this works out to a window of around 70 rows with the default value of windowSize=21. This is a Good Thing because it means that when a user starts scrolling down, the window provides a buffer of offscreen rows that are ready to be displayed.

So that's all good. The issue I'm having is when a user filters the list. My app provides a button which applies a filter to the list by some condition. When the SectionList re-renders after this button is pressed, I would expect the SectionList to only render initialNumToRender rows, and then release the JS thread, and then incrementally re-populate the window using the same procedure as in the initial render.

Actual Behavior

However, what actually happens is that SectionList re-renders every row in the window at once, locking up the JS thread until they have all re-rendered. In practical terms, this makes my application appear to lag a lot, as it takes several seconds to render all 70 rows. So my question is: why does SectionList re-render the whole window before releasing the JS thread? Is this a bug? Is there a way I can get it to only render initialNumToRender rows on a re-render, and then re-populate the window gradually for a smoother user experience?

Reproducible Demo

I tried to create a demo but couldn't get snack to work very well.

UPDATE: I got a demo working : https://snack.expo.io/H1osQ8fiW

I've used a FlatList since it's simpler than a SectionList, but the same problem is there. I've set updateCellsBatchingPeriod to 4 seconds to make it clear when the batched updates are happening.

  1. When the app loads, open the console. When a row is rendered, a log message appears. Every 4 seconds, 10ish messages should appear, corresponding to a batch of offscreen rows being rendered.
  2. Don't scroll up or down – you don't need to. Wait until new messages stop appearing – this will take about 30 seconds due to the large updateCellsBatchingPeriod I've set. It will end up rendering about 70 rows.
  3. Now press the "Toggle data" button at the bottom of the page. This will toggle the data set being displayed. You will see a noticeable stutter in the UI, and then the [A1, A2, A3...] values will be replaced with [B1, B2, B3...] values.
  4. Look at the console when you press the button. Even though there is only space on the screen for <10 rows, you'll see that all 70 rows are immediately re-rendered, with none of the batching that the first render displayed.
JavaScript Stale

Most helpful comment

I can confirm this is a bug. This is happening to me too!

All 14 comments

I'm experiencing the same issue. On re-render, all cells are constructed immediately (hundreds in my case).

Could you try to provide a repro? I already had a similar issue but it was because I was returning invalid data from getItemLayout.

Interesting – I'm not using getItemLayout as my rows are dynamically sized. I'll have another go at snack and see if I can get a repro going.

Ok try this: https://snack.expo.io/H1osQ8fiW . I've used a FlatList since it's simpler than a SectionList, but the same problem is there. I've set updateCellsBatchingPeriod to 4 seconds to make it clear when the batched updates are happening.

  1. When the app loads, open the console. When a row is rendered, a log message appears. Every 4 seconds, 10ish messages should appear, corresponding to a batch of offscreen rows being rendered.
  2. Don't scroll up or down – you don't need to. Wait until new messages stop appearing – this will take about 30 seconds due to the large updateCellsBatchingPeriod I've set. It will end up rendering about 70 rows.
  3. Now press the "Toggle data" button at the bottom of the page. This will toggle the data set being displayed. You will see a noticeable stutter in the UI, and then the [A1, A2, A3...] values will be replaced with [B1, B2, B3...] values.
  4. Look at the console when you press the button. Even though there is only space on the screen for <10 rows, you'll see that all 70 rows are immediately re-rendered, with none of the batching that the first render displayed.

Hope that makes sense. Let me know if anything doesn't work. Thanks very much!

I think this is normal, we have to render a decent amount of row to avoid fill rate issues (having rows not rendered when scolling fast). Like you can see row rendering is batched so it should not block your UI and cause the app to be not responsive.

Thanks for replying so quickly. I suppose I'm confused as to why the batching behaviour is inconsistent – since the initial render with data set 1 is batched, it feels like a bug that the second render with data set 2 is not also batched in the same way?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. If you think this issue should definitely remain open, please let us know why. Thank you for your contributions.

This continues to be an issue that affects us – it leads to our app performing poorly on "old" devices (eg, iPhone 6).

FlatList and SectionList is bad performance. Try this,please. may be it is a surprise for you

https://github.com/bolan9999/react-native-largelist

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Maybe the issue has been fixed in a recent release, or perhaps it is not affecting a lot of people. If you think this issue should definitely remain open, please let us know why. Thank you for your contributions.

This is still an issue for us.

I can confirm this is a bug. This is happening to me too!

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions.

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.

Was this page helpful?
0 / 5 - 0 ratings