facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
117.77k stars 24.15k forks source link

[FlatList] FlatList and VirtualizedList Scroll performance is laggy after 30+ rows . #13413

Closed PARAGJYOTI closed 3 weeks ago

PARAGJYOTI commented 7 years ago

Description

Flatlist or VirtualizedList Scroll lags horribly after 30-40 rows . I am fetching the same data on onEndReached . Upto 30 rows its looks fine , but after that scrolling drops several frames . When I disable virtualization , scroll becomes normal but but responsiveness goes away . I tried disabling virtualizing on scrolling velocity this way .

  isVirtualizationTrue(e){
  var dOffset=(e.nativeEvent.contentOffset.y- this.state.lastOffset)
  var dt=(e.timeStamp-this.state.lastTimeStamp) 
  var velocity = dOffset/dt

  var isInstant=velocity-this.state.lastVelocity>.01

  if(velocity<1 && !isInstant){

      return false
  }
  if(velocity>1){
      return true
  }
  if(velocity <.25){
      return  true
  }

}
But again , there's problem for the unmounted Component that are removed from the views , which takes long time to show up again .

Is there any way to improve the scroll performance ?

Here's my sample code

  <FlatList

    shouldItemUpdate={(props,nextProps)=>
       { 
         return props.item!==nextProps.item

}  }

   onEndReached={this.onRefresh.bind(this)}
   onEndReachedThreshold={200}

    onRefresh={this.onRefresh.bind(this)}
    refreshing={this.props.Feed.isFetching }
    data={this.state.items} 
    renderItem={this.renderItem.bind(this)}  />

My data is sort of large and nested object by the way . And data contains High quality Images . Per row there's two Items . But I implemented it without using numColums for testing with Virtualizedlist instead of Flatlist , but same result . Is it due to the High quality Image or I am doing something wrong ?

Additional Information

jpshelley commented 7 years ago

Its most likely due to the nested components as you mentioned (nothing to do with HQ images). I've had a similar issue, right now I am not providing a shouldItemUpdate method, and instead deailing with shouldComponentUpdate for each of my nested components in the list. Its more work but its helped with the responsiveness. Its still not perfect though so I'd like to hear what others say as well. When I switched to that pattern I also ended up getting the dreaded:

VirtualizedList: You have a large list that is slow to update - make sure shouldItemUpdate is implemented effectively and consider getItemLayout, PureComponent, etc. 

statement again, but when shouldItemUpdate is provided I don't see that message even though the list is less responsive.

PARAGJYOTI commented 7 years ago

@jpshelley hey man , Thanks for the idea . Actually you saved me , but in an another way . I only upgraded to 0.43 just for VirtualizedList thinking it would have great performance . But it turned out it's not mature enough and lots of bugs to be fixed yet . Scrolling was quicker (i mean it fetches data before the onEndReach) but laggy (i mean shaky) . Implenting shouldComponentUpdate makes the scroll performance more horrible (but responsiveness got fixed) . I thought Listview had horrible performance as I didn't implented the shouldComponentUpdate in rederRow. But today I again tried ListView with shouldComponentUpdate and it was great . I am going for ListView again . 0.43 has lots of issues , navigator performance is not good as 0.41 . Anyway waiting for the WindowedList which is as experimental. Hope it fixes all problems.

SunburtReynolds commented 7 years ago

I'll add one more piece of information that might be relevant for a fix:

When I was investigating the issue mentioned above, I spent a lot of time pulling out pieces of code that could cause unnecessary updates. Additionally, I implemented shouldItemUpdate so that I could understand what was causing the FlatList to re-render its items so often. That's when I noticed something peculiar. Once you get past about the first 30 items, shouldItemUpdate gets called a ridiculous number of times event though there is no diff between new and old item props for either the items or the FlatList. In my case, I was seeing a print statement embedded in shouldItemUpdate (no, the print wasn't causing the lag) written to console nearly 1000 times for a single slow scroll.

The only way to prevent the jumpy scrolling behavior was to set disableVirtualization to false.

PARAGJYOTI commented 7 years ago

@SunburtReynolds Hey , you are absolutely right . And there's also an another issue with the new the updated version of Flatlist , which have separated the Fillrate function . The Fillrate logic was not performing good and it causes shakiness during scroll as is preassumed height was not equal to the height after rendering . Lots of bugs has to be fixed . So I downgraded to listview again . You can achieve a similer kind of smooth scroll in listview using dynamic onEndReachedThreshold . Suppose for a datasource of length N , you can set the threshold as n*N where n is a constant floating point and N is dynamic length.

SunburtReynolds commented 7 years ago

We need to be able to control the viewabilityThreshold offered by the FlatList. Otherwise we would most likely revert to ListView as well.

st0ffern commented 7 years ago

I have the same issue, but got it solved 😃 Just made a fix for it. I solved it by clearing all items that are not in the viewport to free memory and CPU usage.

see more of the fix here: https://github.com/stoffern/react-native-optimized-flatlist 😉

gp3gp3gp3 commented 7 years ago

@stoffern I'm already replicating this behavior with my code base using FlatList, as I'm rendering lots of images / videos. But unfortunately with Android FlatList behaves entirely differently and keeping the viewableItems in state doesn't work. The initial rendering renders all the items in the list. Have you run into this with your library?

st0ffern commented 7 years ago

@gp3gp3gp3 try my library and see if it works. I had the issue you describe and it works fine for me after i use the OptimizedFlatList

I dont keep them in the state as that would cause the FlatList to do a full re-render everytime a item changed. Look at my code and see if that makes more sense 😉

nickkapoor commented 7 years ago

Tried different ways and methods with Flatlist; spent 2 days experimenting with it, but its not prime time if you are looking to render pics and videos in a list. ListView still works smoothly.

ShahidAli786 commented 7 years ago

Add the following props in your Flatlist its work onEndReachedThreshold={1200}

raarts commented 7 years ago

Doesn't that basically turn the FlatList into a ListView though?

SunburtReynolds commented 7 years ago

FWIW, version 0.45.1 seems to have fixed the jumpy scrolling issue!

atpunk commented 7 years ago

Wrapping the whole Flatlist in a ScrollView worked for me.

dzpt commented 7 years ago

@stoffern have tried your lib and it doesn't help. I have to switch back to ListView after all.

sitompul commented 7 years ago

is there a fix yet?

stokesbga commented 7 years ago

I remember when I started this project at 0.29... feels like Ive been chasing the magic dragon updating every minor version. +1 for listview

sitompul commented 7 years ago

I changed my render item component into PureComponent and it works fine

bduyng commented 7 years ago

+1

naqvitalha commented 7 years ago

We've had lot of problems with react native listview as well so, we built one ourselves, just open sourced it. We've tested it with 5000 items works like a charm. The reason it's so fast is that it uses a quick staggered grid algorithm and recycles views (based on types) instead of destroying them once they're out of view port. Please try it out and give us feedback https://www.npmjs.com/package/recyclerlistview

PARAGJYOTI commented 7 years ago

Hei @naqvitalha , Its really good to know that you have created an recyclerview library . I was always looking for that . However ,it was for an another reason , I needed a component to build snapped scrollviews like google-play-store apps scroll . React-native currently lacks that library . I wonder why nobody has created one yet . Please let me know if you can add that functionality with snaphelper , I believe it's possible only using recyclerview . Here re some links you can have a look .

https://rubensousa.github.io/2016/08/recyclerviewsnap

https://blog.mindorks.com/using-snaphelper-in-recyclerview-fc616b6833e8

naqvitalha commented 7 years ago

@PARAGJYOTI The listview that I talked about is not based on native recycler view, it's written from scratch purely in JS, making it cross platform (also web :) ). You can open an issue. We can certainly add this in the future.

PARAGJYOTI commented 7 years ago

@naqvitalha Owh , I thought it was native . Anyway , is flipkart also using react-native for mobile ?

naqvitalha commented 7 years ago

@PARAGJYOTI Yes we are.

blackmiaool commented 6 years ago

I don't know why, but try legacyImplementation, it does the trick

dsvgit commented 6 years ago

+1 have the same performance problem

rahmatkruniawan commented 6 years ago

So am I 😢

naqvitalha commented 6 years ago

Then why don't you guys give RecyclerListView a chance? https://www.npmjs.com/package/recyclerlistview

rahmatkruniawan commented 6 years ago

@naqvitalha In recyclerlistView why need ViewTypes , I not really got it

naqvitalha commented 6 years ago

ViewTypes are needed to efficiently recycle cells. So that similar looking cells are recycled. You can just return one type in layout provider in case you don't need it.

edo1493 commented 6 years ago

I have a horizontal list and I see some delay when I call scrollToIndex. It gets really slow with 20-30 elements.

jslok commented 6 years ago

I had similar performance issues with FlatList and here's what I did to solve it:

  1. Make sure FlatList is not a child of ScrollView as scrollview will render all child components, even those not on the screen in FlatList.
  2. Add prop removeClippedSubviews to FlastList so it removes items out of view. (didn't test if this is redundant to #1)
  3. Implement shouldComponentUpdate for the renderItem component or make it a PureComponent.
gitlovenotwar commented 6 years ago

@justinlok does #1 proven already? Is it bad for a listview/flatlist inside a scrollview?

jslok commented 6 years ago

@gitlovenotwar when i had flatlist within a scrollview, i noticed all flatlist items were being rendered at once. I realize now that the docs say to define a height for scrollview which i dont think i did. That may have been the reason all items were rendered at once.

Anyways, i do not see a reason to use scrollview instead of a simple view. Flatlist still scrolls fine within a view. If somebody can explain it would be great.

nonotest commented 6 years ago

I ended up rolling back to listview for some screens .. flatlist is still super random :(

jankoritak commented 6 years ago

You can try utilising legacyImplementation, via legacyImplementation?: ?boolean prop. Set it to true.

In my case, our x-hundred lines long list with pretty complex item components, which felt really clunky, suddenly became super-fast. 🤔 ❓

There probably are some caveats/limitations to legacyImplementation. In my case, it does the trick though.

Can someone confirm/displace this?

mannol commented 6 years ago

@jankoritak Legacy implementation is plain old ListView. It generally works better but it has a disadvantage of poor performance.

naqvitalha commented 6 years ago

@mannol actually old ListView's performance is better. Problem there is the lack of virtualization which increases memory usage.

mannol commented 6 years ago

@naqvitalha yes, though increased memory usage has a major performance impact on Android. In my observations, legacy implementation behaves the same way as ListView: no virtualisation, so, there is no clear benefit of using FlatList in that case, except for the cleaner API.

SudoPlz commented 6 years ago

@naqvitalha What is that recycleviewlist good for?

In our case we need an infinite list basically. (Making a calendar)

naqvitalha commented 6 years ago

@SudoPlz Check this out https://github.com/naqvitalha/CalenderDemo I actually did something like that. But the project is incomplete (perf wise), issues are listed in the readme. New versions of RecyclerListView has changes which can make this much faster but haven't had the time to update. Btw RecyclerListView is much faster alternative to FlatList with context preservation. Check the app if you want (you can scroll the date list too): https://drive.google.com/open?id=0B90JDy5HZpFxOEpPVmtjaHZkX3M

bolan9999 commented 6 years ago

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

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

bamne123 commented 6 years ago

Hi Guys, I was facing same issue, while experimenting following setting has solved my problem, Hope this will also help others.

Set removeClippedSubviews and disableVirtualization for Flatlist and it will work like charm.

<FlatList removeClippedSubviews disableVirtualization ... />

package.json : "react-native": "0.51.0",

mymai91 commented 6 years ago

@bamne123 it still happens.

bamne123 commented 6 years ago

@mymai91 can you share your code?

mymai91 commented 6 years ago

hi thanks, but It's fixed. I use the way approach from jpshelley

sitompul commented 6 years ago

@bamne123 disableVirtualization is basically translate your code into listview. Why bother using flatlist?

kelset commented 6 years ago

Hey all, quick followup on this issue (since it has been reported a couple more times). It seems like this combination of Android + Flat/SectionList + Images can lead to sub-optimal performances, and the core team is aware of it.

While the issue is investigated, here are few things you may want to try for a FIY solution:

  1. go back and read this comment
  2. go back and read this other comment
  3. make sure you are using the full-range of props to tweaks the params of the Flat/SectionList (remember that they inherit from VirtualizedList and ScrollView) (I personally suggest to enable removeClippedSubviews only on Android)
  4. consider optimising your Fresco configuration

Hope this may help!

skywalkerlw commented 6 years ago

@jankoritak @blackmiaool legacyImplementation is the trick too :) and now my app become flying

jhalborg commented 6 years ago

Going to +1 for legacyImplementation, too. I can't use that, though, as it does not support my header, so I've instead resorted to removeClippedSubviews={Platform.OS === 'android'} as suggested by @kelset for now. It is not as performant as legacyImplementation, but it's better than the default false setting. FWIW, I'm using PureComponent for my rows, and they are all of the same type in a Facebook/Instagram like feed with images, comments and profile pictures.

Anxiously awaiting resolution of this issue, as infinite scrolling feeds with images are the main feature of two of my production apps :-)

wedneyyuri commented 6 years ago

Hi guys,

I have not found an example of how to use PureComponent in my renderItem component. Currently, I'm using an arrow function. Can you give some tips?

What I have tried:

class Item extends React.PureComponent {
    render() {
        return <Text>Hello world</Text>;
    }
}
<SectionList
    refreshing={refreshing || fetchingNext}
    onRefresh={(() => { })}
    stickySectionHeadersEnabled={true}
    sections={this.transformSectionsData(documents)}
    renderSectionHeader={SectionHeader}
    renderItem={Item}
    keyExtractor={this.keyExtractor}
    onEndReached={this.fetchNextPage}
/>

image