React-native: [FlatList] Rows aren't rendered until scroll

Created on 1 Jul 2015  ·  223Comments  ·  Source: facebook/react-native

Hi,
I have list view which displays partially local and partially remote data. Local data are initial for ListView.DataSource. This datasource is set in state of my custom component which wraps ListView and passed in render method to ListView. When are remote data received, new datasource is cloned by cloneWithRowsAndSections method and set to state of custom component. Problem is that are re-rendered only already displayed rows and new rows are rendered after scroll.

renderbug

Is it bug or how I should to force rendering of ListView? With react-native 0.5 it worked but after upgrade to 0.6 it behaves as described above.

Locked

Most helpful comment

I was seeing this same issue on 0.17 but disabling removeClippedSubviews seems to have fixed it.

All 223 comments

+1
Encountering exactly the same problem. Worked with 0.5 but broke with 0.6.

This should be fixed in 0.7

I was experience the same exact problem.

cloneWithRows works fine. but cloneWithRowsandSections doesn't.

Are you seeing cloneWithRowsandSections not working with 0.7?

Also, you might want to try setting initialListSize to a larger number - that might help as a workaround if things aren't fixed in 0.7 for you.

Bigger initialListSize does not help and 0.7 I have not tried yet because vendors dependency. As it will be possible I let you know about 0.7 if somebody will not be quicker :)

Having the same issue with cloneWithRows in version 0.6, was working in 0.5.

Haven't tried it in 0.7. Will give it a try. I know that I also had tried setting the initialListSize to a higher number previously and didn't help.

worked in 0.7

Also not working in 0.7.1

Having the same issue with cloneWithRows in version 0.7.1

For me it works well in 0.7.1.

Does not work in 0.8.0.

Still using cloneWithRows instead of cloneWithRowsAndSections

cloneWithRows works for you yamill? Doesn't for me in 0.8.0.

@coderdave actually cloneWithRows doesn't work for me either. my mistake.

@sahrens @ide @michalraska my scrollY prop was not getting updated. I was basically trying to pass the scroll offset to my component, like so:

    renderRow: function (rowData, sectionID, rowID) {
          return (
            <Row
            key={rowData.id} data={rowData} scrollY={this.state.contentOffset}
             />
          )
    });

But fixing this line allowed me to receive the scrollY prop to my component successfully. I wonder if this change fixes all others issues?

https://github.com/facebook/react-native/blob/757d6d204ae2d743634af64e3f78a4aad9d53f70/Libraries/CustomComponents/ListView/ListView.js#L342-L343

I changed it from:

var shouldUpdateRow = rowCount >= this.state.prevRenderedRowsCount && dataSource.rowShouldUpdate(sectionIdx, rowIdx);

to

var shouldUpdateRow = true;

I've noticed this on 0.11.0-rc as well.

Here are two examples that can reproduce the issue.

Rows: https://rnplay.org/apps/d3DM6A
Rows + Sections: https://rnplay.org/apps/xnyaYw

The temporary solution that I came up with was to scroll the ListView 1pt when it mounts.

let listViewScrollView = this.refs.listView.getScrollResponder();
listViewScrollView.scrollTo(1);

You can uncomment this section inside the example on rnplay.org for testing.

Silly question. Could this have anything to do with using NavigatorIOS? I've had some issues w/ rendering, padding, etc all when a list view was rendered as a child of NavigatorIOS.

@jaygarcia I don't think so since the example's don't make use of NavigatorIOS. Also, in my own project I'm using Navigator instead of NavigatorIOS.

+1 for me, I'm currently encountering this behaviour when using Navigator. I'm on 0.11 and I use cloneWithRows.

As workaround, I had to use @christopherdro's solution, but I would need to scroll it sufficiently much to get my items rendered (they are big).

Interestingly, the work around is related to the workaround for this bug: https://github.com/facebook/react-native/issues/1878 so my final code is:

    listViewScrollView.scrollWithoutAnimationTo(80);
    listViewScrollView.scrollWithoutAnimationTo(-80);

ok, using the code mentioned above, I got some bizarre bugs in nested ScrollViews: After mounting, an onScroll event was triggered which repositioned my initial scroll offset on those views.

Still a bug in 0.12.0. Here's the view hierarchy:

Same issues as @Sidnicious on 0.11.0. My app has had this issue since an early version of react-native. Everything works as expected if I move the ListView out of the Navigator hierarchy.

I am experiencing the same issue using 0.13.0-rc

I have this issue in 0.13.1 with only this:

    constructor(props) {
        super(props);
        var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
        this.state = {dataSource: ds.cloneWithRows(Array.apply(null, {length: 100}).map(Number.call, Number))};
    }
    render() {
        return (
    <ListView
        style={{paddingTop: 20, flex: 1}}
        dataSource={this.state.dataSource}
        renderRow={(rowData) => <Text>{rowData}</Text>}
    />
    )

For me this problem just showed up when upgrading from 0.14.2 to 0.16.0-rc. The fix that worked for me was to set the pageSize prop on the ListView to 3. 2 didn't work for me, but 3 worked just great.

¯_(ツ)_/¯

same issue here with 0.15.0 , tried all the tricks, no luck. ios 9.1 iphone 6

@nicklockwood - can you help out here? Seems like there are still some bugs.

0.14 ,same issue

Linking a similar LisView issue: https://github.com/facebook/react-native/issues/4728

I'm seeing this in 0.16.0. Has anyone placed this on the radar on Product Pains? This one is becoming quite painful. :/

I've posted this on Product Pains. If it's still affecting you feel free to vote it up on there.

0.16, same issue

Issue still persists in 0.17 using cloneWithRows on the dataSource. The list only renders items when it's being scrolled.

I was seeing this same issue on 0.17 but disabling removeClippedSubviews seems to have fixed it.

+1
I was seeing this same issue on 0.17 but disabling removeClippedSubviews seems to have fixed it.

For me it still happens in 0.17 with or without removeClippedSubviews. What I did notice was that my issue might be related to a manually set contentOffset and initialListSize. If the offset is beyond the height of the elements that would be rendered for the initialListSize, it wouldn't render the required elements until the user scrolled.

There also seems to be an issue with lists that have flexDirection: 'row' set. When I remove this property all items render. If it's set the List initially renders only two items.

For the removeClippedSubviews problem when using Navigator.
You might want to check https://github.com/machard/react-native-advanced-navigation where the problem do not occur (probably because the render is delayed to after the view is really on screen)

There is also issue with flexDirection: 'row' and flexWrap: 'wrap' set in contentContainerStyle property. The workaround that works for me is setting pageSize.

react-native version: 0.19.0

thanks @jittuu setting the pageSize prop did it for me too!

setting pageSize to what?

@gre it depends on your layout. If your views are arranged into rows, pageSize should be a multiple of the items per row. You can experiment with the exact value, to see how it affects performance.

You should also set initialListSize large enough to fill the whole screen.

You might find the explanation in this commit useful: https://github.com/facebook/react-native/commit/e7005f7f5429422b6f2e9c2aa29c9d57bda7b77a

@jittuu This worked for me; I was also experiencing the bug when using flexWrap.

thanks @jittuu set the pageSize with the value 2 solved me the problem too!!

still getting the problem when doing a scroll on the listview, navigating the app and going back.
pageSize didn't help (i've tried different value from 1 to 60).

@gre are you setting initialListSize large enough? Is there a repo we can view?

@jaygarcia @nicklockwood I have a demo to reproduce this bug, repo url: ListNotRender

step:

  1. open the app.
  2. click the tabbar item "#1".
  3. list item not render until any scroll..

I am sure this bug relative the removeClippedSubviews prop of ListView, and the bug only occurs when it set true.

This is still happening for us on RN 0.26.0. Same situation here:

  • removeClippedSubviews={true}
  • ListView is rendered off-screen
  • when going to that screen, the listview is slipped until the user interacts with it

cc @javache

@javache I manually added that line locally and it did not fix the issue.

I also applied this one on top of it https://github.com/facebook/react-native/commit/1048e5d3445094393298d4e818ff04c41f4e56a7 but no success either. ;)

the single https://github.com/facebook/react-native/commit/1fcd73f3841d5afbabfa3adecfb7d4036d91a60e commit on top of RN 0.28 still creates the bug for me too.


Investigating a bit on this, I'm wondering if the ListView is still updating in background (e.g. during a scroll update) even when it is no longer visible?

Because, in fact, the bug is easily reproducible to me if I scroll on the view, do a navigation when it's still scrolling with 'gravity', wait a bit and click back. The full screen will be white until I scroll again.
But that's basically the only scenario when this bug happen to me. Is this ~ the same repro scenario to everyone, or is the bug wider than this?

That scenario is not so far from @janmonschke https://github.com/facebook/react-native/issues/1831#issuecomment-22799032 .


It's hard to figure out the reason of this bug, but here is my 2-cents:

It sounds that the rows get rendered _white_ during background. When making the scrollview visible again _(e.g. go back)_, they are still white, and they don't get updated _(because they have no reason to be updated, they are cached)_ until a new scroll happen.

Would it fix the bug if the ScrollView would render its children (updateClippedSubviews?) only if it is still visible? ( if the rows are not rendered during background, they never would be shown white again)

@javache 1fcd73f can fix the case ListView with one or two row that not filled full scree. in that case even scroll ListView can't reshow the list row.

I find out a simple way to reproduce the bug:

  1. Show a ListView with removeClippedSubViews = true;
  2. Touch a row enter next page;
  3. Rotate screen orientation 90;
  4. Rotate screen back;
  5. Back to ListView screen;

ListView will be blank, with rows more than a screen, scroll the ListView will reshow, with rows less than a screen, even scroll ListView can't reshow any thing. with 1fcd73f scroll ListView can reshow :)

  1. Still seeing this problem as of 0.26.3
  2. On my non RN project I saw the problem in native Android.

The solution we chose was to do a scrollTo 1px shift ahead and back onRefresh. It's a work around but it has worked in both instances.

requestAnimationFrame(() => { this.listview.scrollTo({y: 1}); });

@JBerendes yes this hack works.

here is a more sophisticated workaround that doesn't break user's scroll:

class ...YourListAbstraction {

  _scrollY = 0;
  _lastTimeScrolled = 0;

  scrollHackToWorkaroundWhiteBug (amount) { // call at appropriated time, with -1 or 1. if possible alternate so you don't change the actual scroll over calls xD
    const { list } = this.refs;
    if (!list) return;
    if (Date.now() - this._lastTimeScrolled < 500) return; // don't mess with user scroll
    list.getScrollResponder().scrollTo({
      y: this._scrollY + amount,
    });
  }

  onScroll = ({ nativeEvent }) => { // give onScroll={this.onScroll} to ListView
    this._scrollY = nativeEvent.contentOffset.y;
    this._lastTimeScrolled = Date.now();
  };

}

to put in your ListView abstraction. Then you need to call scrollHackToWorkaroundWhiteBug at appropriated time. For me it's every time I change screens (before and after the transition).

I think this is related to the ListView's scrollRenderAheadDistance (because it renders the un-rendered rows after being touched) and I have managed to fix it by setting the scrollRenderAheadDistance to a value greater than 1800. There is definitely a correlation between scrollRenderAheadDistance and the number of rows that are rendered but it's inconsistent. I've found that it usually renders as follows (although sometimes it will render all rows):

| scrollRenderAheadDistance | Number of rows rendered |
| --- | --- |
| 1000 | 2 |
| 1200 | 2-5 |
| 1400 | 5 |
| 1600 | 6 |
| 1800 | 7+ |

I'm rendering a ListView with rows of 80pt height. I've derived a formula that should help you set the exact right scrollRenderAheadDistance for your ListView:

scrollRenderAheadDistance = 680 + (ROW_HEIGHT_IN_PIXELS * INITIAL_PAGE_SIZE_IN_PIXELS)

However, I don't understand the significance of 680 (or 340 * 2).

EDIT: This solution works in Debug but not Release scheme (for iOS)...

Ok, setting removeClippedSubviews to false solves the problem for me.

I didn't encounter this until upgrading from 0.26 to 0.29, so something was introduced in 0.27, 0.28, or 0.29 (I haven't tested against 0.27 or 0.28 yet but I can if that will help).

@gnestor @nihgwu I'm pretty sure the bug is not a recent regression. This issue has been created one year ago, and I have personally experienced it since RN 0.13 and always used workaround to "survive" with ListView. not using removeClippedSubviews makes big list laggy.
Maybe there were attempts to fix it or at least to reduce cases where the bug appear, but this still happen roughly like what was reported in this initial issue message.

@gre but I still think #8607 was introduced in in 0.29, it's really troublesome going to blank view, I just revert the change of https://github.com/facebook/react-native/commit/1048e5d3445094393298d4e818ff04c41f4e56a7 and everything works great as before without turning off removeClippedSubviews

Set initialListSize to a proper value and set removeClippedSubviews to false solve my problem

Got this problem after upgrading from 0.28 to 0.29.
removeClippedSubviews={false} solved the problem.
Also, in my case, I'm updating ListView height on componentDidUpdate().
Wrapping the height-updating code in componentDidUpdate() with setTimeout with custom timeout also helps.

I did also experience this but it was on one ListView that was populated with local data. I also was only seeing this on iOS and after I upgraded to 0.29. The ListView in question uses this.state.dataSource.cloneWithRowsAndSections and componentDidMount is when I load it.

And so removeClippedSubviews={false} addressed this.

Setting removeClippedSubviews on my ListView component worked for me as well. I was only experiencing this issue with the Release scheme on 0.29 when using the default Navigator component.

I have the same problem, I have set flexDirection: 'row' and flexWrap: 'wrap', I'm on RN 0.29.2. On other listviews (where I haven't set flexDirection: 'row') I don't have that problem.

Quick fix like other posters have said is to set proper initialListSize and pageSize, but I believe those will make the app less responsive, for example if you have some filters which will show just the parts of a list of items that you have inside the listview, tapping between filters will be slow because the listview is trying to rerender the whole visible area.

On other listviews I've solved this by setting initialListSize=0 pageSize=1. But I cannot on the grid listview (flexDirection: 'row' and flexWrap: 'wrap').

I'm hitting this on RN v0.31.0-rc.0.

Adding removeClippedSubviews={false} seems to have fixed the issue.

"removeClippedSubviews={false}" seems not to be a good solution, it will render all rows and has memory manage problem. Does someone have better solution except "listView. scrollTo" and "removeClippedSubviews" ?

This issue is far far worse on ios 10 beta with RN 0.31 :-(

removeClippedSubviews didn't help me on Android, had to move from ListView to ScrollView instead.

A really huge bug. I met it too.

Make sure everyone upvotes it on Product Pains. It's already pretty high up, but not quite high enough to land in someones inbox yet :-( Here's the link

At this point I'd say this is probably the same issue as #8607. Check it out for my plan how to tackle it.

Continues to exist in 0.33.0

Still exists in 0.32

I met this bug yesterday, and setting removeClippedSubviews = {false} worked.

+1 on 0.33

Yee, me to, same problem! +1
0.33,
Edit:
removeClippedSubviews = {false}, fixed the issue for me too.

+1

Could you try out this patch https://gist.github.com/majak/b0ee1fb6ca725d2cf810d2f0ab394f2e (from #8607) and let me know whether it helps with this issue?

@majak Thank you for sharing this patch. We just apply it, now this issue is gone (RN 0.34).

@majak this seems to fix it for me as well, good job!

this is happening on RN34, when removeClippedSubviews={true}

Had this problem on React Native 0.34.
The problem only occured when I had:

    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',

on the listview styling.

initialListSize={100}
fixed it for me

@majak are you planning to create a PR with that patch?

@janmonschke yup! Follow the linked issue for updates.

Had this problem on React Native 0.36

I can confirm this happens on 0.36

It perfectly works in emulator, in debug mode only, but never on a device. The list never works on a device, tried everything recommended from the above, even replaced the ListView with ScrollView, all I see on my android device is navigator. (Even tried putting the list outside of navigator)
0.37

I can still found the issue on 0.36 but thanks for the work around!

I am still seeing it at 0.36.1

I am still seeing it at 0.36.1

exist in 0.31
initialListSize={1} solve it for me

Had this problem on React Native 0.38

renderScrollComponent={(props) => <ScrollView style={flex: 1} /> }

esto soluciona el problema en la versión 0.38, no se si en versiones anteriores también funcione.
this archive the problem in versión 0.38, i don't know if work on early versions

This problem exists on 0.36.
Could not get it work with removeClippedSubviews = {false} and initialListSize={8}

more than 1 year passed, this issue is still open??

still exists on 0.39. set removeClippedSubviews = {false} works.

"removeClippedSubviews={false}" seems not to be a good solution, I have a new solution and it works for me. Paltform iOS. RCTView.m file
screen shot 2017-01-05 at 10 55 30 am
screen shot 2017-01-05 at 10 58 25 am

should I create a PR ?

Has anyone tested on RN0.40, I don't encounter with this issue any more after upgraded to RN0.40

This problem exists.
1, Show a ListView with removeClippedSubViews = true;
2, Touch a row enter next page;
3, Rotate screen orientation 90;
4, Back to ListView screen;

ListView will be blank, with rows more than a screen, scroll the ListView will reshow

@endpress I've followed your steps, still can't reproduce it, in using RN0.41RC0

Have this issue in 0.38.

My workaround is changing the paddingTop of ListView between 0 and 1,so the ListView will get refreshed everytime.

You may try to change the backgroundColor, border, etc. Please let me know if other properties works.

@nihgwu still exists on 0.40.0

This is the issue
bug-3

@endpress the change (https://github.com/facebook/react-native/issues/1831#issuecomment-270552011) works for my case, and it may help facebook/react-native#8607 as well.
i know the patch may be still far from perfect, but I still suggest you to create a PR.

for me even setting initialListSize={0} fixes it rn 0.40.0

@majak Is there anything good news to this fatal problem?

FlatList is still experimental, so no guarantees on it having fewer bugs or changing in backward incompatible ways, but you can try it out at your own risk.

@sahrens @gre Is it possible to build an abstract layer on top of FlatList that provide the same API as the old ListView so that our old application code could keep the same.

@sahrens FlatList is implemented with UITableView?

Once FlatList is stabilized and no longer experimental, we might make an adapter with an identical API as ListView, or maybe we will swap out the implementation of ListView under the hood.

It doesn't use UITableView. It actually doesn't use any new native code at all, it's all just JS with our existing native/framework primitives - you can look at the implementation here: https://github.com/facebook/react-native/commit/a3457486e39dc752799b1103ebe606224a8e8d32

@sahrens thank u, it's quite cool and useful.

@savanthongvanh setting initial size to 0 has the effect of rendering all items in the ListView on initial load. Be careful with this if you have a lot of items.

Also dealing with this rn 0.41.2. Does someone have a simple implementation of FlatLIst that I can copy, really hoping for a solution soon.
Thanks,
ron

FlatList (andVirtualizedList) are in master if you want to play with them.

Here's a simple way to get started now with FlatList for anyone dealing with this: https://hackernoon.com/react-native-new-flatlist-component-30db558c7a5b#.xnp03gd2u

Set initialListSize to a proper value and set removeClippedSubviews to false solve my problem

Thanks @hoperce , with removeClippedSubviews works for me, the initialListSize cause choppy scroll

I have RN .42 and I don't see any experimental libs in here for the FlatList, so I'm stuck until I can upgrade (not for a little while) with this issue.

You can always copy the FlatList code into your app, even if you aren't using the latest version of RN.

I can still reproduce the "full view goes white when going back" bug with FlatList. I'm wondering if it's not more related to ScrollView.

The issue with briefly going white is totally different and unique to FlatList. We're working on mitigating it but it's a tricky consequence of asynchronous windowed rendering. The bug this issue refers to is where no content is visible on initial render until the user scrolls, which I hope is fixed with FlatList.

I have also encountered this issue with ListView (and FlatList as well).

I just discovered that I could get the list to render properly by resetting my datasource to [] in the constructor of the view and then resetting it to the list of items in a setTimeout()

FYI My listview is embedded in a ReactNavigation StackNavigator in a TabNavigator.

also, the hack to trigger a 1px scroll programmatically still works both for ListView / FlatList. but you need to call it at the right lifecycle (usually when you come back to screen).

@gre: do you have a repro app for the issue with FlatList? I would like to get that fixed asap!

@gre , do you have example code for that? And do you mean in componentWillMount for that component encompassing a ListView?

@ericvicenti - looks like it takes quite a few other pieces of code from RN. I was worried it was going to be borrowing bits and pieces from elsewhere. I may pull it out. Maybe throw it in a repo for other people to use -- is that allowed?

@sahrens sorry it's in my company app, but maybe I can try to create a blank example to repro it!

@natdm a bit above in a comment: https://github.com/facebook/react-native/issues/1831#issuecomment-231069668 – this is the basic idea, i have more or less diverged from this with a system that will alternate between a -1px and 1px so it does not visually accumulate a scroll over time XD huge hack

I'm having this issue with SectionList - blank on initial render until I scroll. @sahrens is there a fix in sight?

@smkhalsa do you have a clear repro? Does setting removeClippedSubviews={false}

@sahrens It looks like setting removeClippedSubviews={false} does fix this for me. Without this, I get a blank screen every time I navigate to this particular view.

I will try to isolate the issue in a fresh repo and post it if I can.

same for me (removeClippedSubviews definitely is a trigger for the bug). we just had an easy repro in our app that have lists inside react-native-tab-view . I don't know however if a simpler example will repro it.

see the 2 last paragraphs of my answer here https://github.com/facebook/react-native/issues/1831#issuecomment-228775913

I think it might be something around that (just an hypothesis):

(1) list get rendered in a background tab, since they are 'out of the bounding box' removeClippedSubviews will assume they are not here and will not render anything (white)
(2) when the tab goes to focus, and because that tab probably use something like a <StaticContainer>, nothing get 'refreshed', it's still white
(3) as soon as user will 'scroll', you refresh the removeClippedSubviews logic that now determine the list cells are visible and refresh them.

Thanks for the lead @gre

still exists on RN 0.41.2, android is ok, just the ios10, set removeClippedSubviews={false} can resolve this issue. My listView is small, so not a big problem. The listView inside a stackNavigator of tabNavigator(react-navigation).

 render() {
    return (
      <View style={{ flex: 1, justifyContent:'center'}}>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this._renderRow.bind(this)}
          removeClippedSubviews={false}
        />
      </View>
    );
  }

Same issue here. Nothing else to add, other than removeClippedSubviews={false} solved it for me as well.

@agentilela Thanks for the fix, same issue here

Setting removeClippedSubviews={false} fixed it

this issue exists in react-native-maps MapView which AFAIK does not use ScrollView and has nothing to do with ListView.

The problem is still happening in RN44, initialListSize={200} does solve the problem, but I don't think it is a good long term solution as it takes some time to render before displaying it out. It's obvious in a list view that is more than 100 rows.

p.s. removeClippedSubViews={false} does not solve my problem

removeClippedSubViews={false} worked for me for ListView.
Also, FlatList has the same problem.

I have the same problem with ListView/FlatList and react-navigation.
I have fixed it with lazy: true in the TabNavigator options and removeClippedSubViews={false} in ListView

I can also confirm that the bug happens in RN 0.44.0:

Happens when using react-navigation + TabNavigator + (ListView or FlatList).
When you go to a tab it looks empty. Only when you scroll a bit, the list is shown.

The only tab where it's not happening is in the initialRouteName of the TabNavigator

As mentioned, setting lazy: true on . the TabNavigator solves this.

same issue here, fixed with removeClippedSubViews
as described in duplicate issue https://github.com/facebook/react-native/issues/14069
I created a repo to reproduce the issue https://github.com/jcharlet/react_native_listview_bug if it can be of any help.

Also this issue doesn't happen when using 'react-native-router-flux' instead of 'react-navigation' for the same use case

Some problem with react-navigation in TabNavigator. removeClippedSubViews={false} does not help.

Same problem for me. Using react-navigation with TabNavigator and a StackNavigator as a child in the tab in question, and plain ListView. RN 0.44 / Expo 17, and neither removeClippedSubviews nor lazy helped for me :-/

removeClippedSubviews helped me to fix IOS version, but for android i had to use initialListSize

With

"dependencies": {
    "react": "16.0.0-alpha.6",
    "react-native": "0.44.2",
    "react-navigation": "1.0.0-beta.11"
}

and lazy: true in TabNavigator's TabNavigatorConfig everything renders well:

const AppNavigator = TabNavigator({
  HomeTab: {
    screen: HomeScreen,
    path: '/'
  },
  PeopleTab: {
    screen: PeopleNavigator,
    path: '/people',
  }
}, {
  lazy: true
});
const PeopleList = ({ people }) => {
  return (
    <FlatList
      data={people}
      renderItem={({item}) => <Text>{item.name}</Text>}
    />
  );
};

Upgrading to
{
"react-native": "0.44.2",
"react-navigation": "1.0.0-beta.11"
}
worked for me, lazy=true doesn't seem to be required for StackNavigator

{
"react-native": "0.44.2",
"react-navigation": "1.0.0-beta.11"
}
lazy=true
removeClippedSubViews={false}
doesn't work

This is really interesting, I'd like to see when this 2 years bug will finally be closed.

I upgraded to react-native 0.44.2, react-navigation 1.0.0-beta.11, and set "lazy=true" on the TabNavigator in question and that fixed it. Perhaps just setting lazy=true would have done the trick, but I had already updated.

@jhalborg I fixed the issue for lists with FlatList & removeClippedSubViews={false}.

I'm using react-navigation and removeClippedSubViews works for me with this structure:

modal stack -> present tab stack -> include multiple tabs with navigation stacks

My problem was the lack of performance so I've made removeClippedSubViews value based on a state value so I can toggle it on/off depending on the whether the screen is loading.

I have some lists that reload while on another tab so I use something like following:

constructor (props) {
   super(props)

   this.state = { removeClippedSubViews: false }
} 

componentDidMount () {
  this.setState({ removeClippedSubViews: true })
}

resetData () {
  const callback = (newRecords) => {
    this.setState({ removeClippedSubViews: true, records: newRecords })
  }

  this.setState({ removeClippedSubViews: false, records: [] }, () => {
    someDataHelper.reloadData(callback)
  })
}

render () {
  return (
    <ListView removeClippedSubViews={this.state. removeClippedSubViews} />
  )
}

I hope this helps someone as it had been annoying me for a while but ended up being a pretty simple solution.

It looks like this issue can now be closed, for the following reasons:

  • We're recommending people use FlatList/SectionList now, no enhancements will be coming to ListView.
  • It seems like a reasonable workaround has been identified in the thread.

Thoughts?

Isn't this a real bug? If yes, why closing the issue?

About the reasons:

  • It also happens on FlatList, so it isn't a ListView only problem.
  • Removing removeClippedSubViews seems like a hack with performance costs, not a reasonable workaround in my opinion.

Confirming that this is still an active bug within FlatList/SectionList.

There's also a few issues relating to the performance of FlatList/SectionList which means some people can't yet move from ListView, seems odd to have deprecated it already.

Could something like what I posted above be integrated into the components themselves?

What problems have you had migrating off ListView?

13727

I'm not seeing any specifics of where ListView is better. Note you can increase the window size or turn off virtualization if that's an issue for you, since ListView doesn't do any of that.

I'd wait for them to respond to the questions you just asked before moving on from it but it's beside the point, this issue still occurs on FlatList and should remain open.

Just for general community knowledge.

I came across this issue in my scenario where I am using Nested ListView's i.e. ListView's within a ListView row. My solution was fixed by applying removeClippedSubviews={false} to the nested ListView's.

react-native-cli: 2.0.1
react-native: 0.41.2

It is impossible to have a smooth scroll in large lists using ListView / FlatList / VirtualizedList / WindowedListView (whatever) in old hardware like iPad 3 / iPad Mini running iOS 9.x.

Note: I'm talking about flat lists, no images, only text components in rows.

Someone could share a functional example with a dataset of 7000 records where the scroll is smooth and not be choppy, im try all attributes configurations and no luck 😢

Agreed - I'm clearing out-of-view cells too for memory management. It's a shame that such an essential component in many apps is so lacking in performance for a lot of use cases. I'll be diving deep into it in the next week or so to see if there's any significant performance gains to be made.

On 10 Jun 2017, 3:30 PM +1000, Ariel Falduto notifications@github.com, wrote:

It is impossible to have a smooth scroll in large lists using ListView / FlatList / VirtualizedList / WindowedListView (whatever) in old hardware like iPad 3 / iPad Mini running iOS 9.x.
Note: I'm talking about flat lists, no images, only text components in rows.
Someone could share a functional example with a dataset of 7000 records where the scroll is smooth and not be choppy, im try all attributes configurations and no luck 😢

You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

Awesome @lprhodes, here I share a simple use case using FlatList with ~ 200 records, this example is choppy on an iPad 3 with iOS 9.

import React, { Component } from 'react';
import {
  FlatList,
  StyleSheet,
  View,
} from 'react-native';
import Expo from 'expo';
import {
  Text,
  ListItem,
  SearchBar,
} from 'react-native-elements';

class Feed extends Component {

  loadFeed() {
    const {data} = this.props;
    const sections = [];
    data.forEach((value, index) => {
      const sectionName = value.name_line_sp;
      const section = sections.find((section) => {
        return section.title === sectionName;
      });
      if (section) {
        section.data.push(value);
      } else {
        sections.push({
          title: sectionName,
          data: [value]
        });
      }
    });
    if (__DEV__) {
      console.log('Sections size', sections.length);
    }
    return sections[0];
  }

  componentDidMount() {
    this.flatListRef.scrollToOffset({
      animated: false,
      offset: 48
    });
  }

  render() {
    const feed = this.loadFeed();
    return (
      <View style={{flex: 1}}>
        <Text h5 style={styles.section_title}>{feed.title} ({feed. data.length})</Text>
        <FlatList
          style={{flex: 1}}
          ref={(list) => { this.flatListRef = list; }}
          debug
          ListHeaderComponent={() => (
            <SearchBar
              lightTheme
              placeholder='Search...' />
          )}
          data={feed.data} // ~217 records
          keyExtractor={(item) => (
            item.code_group
          )}
          renderItem={({ item }) => {
            return (
              <ListItem
                hideChevron
                key={item.code_group}
                title={`${item.name_group_sp}`}
                subtitle={`${item.price_prod}`}
                containerStyle={{ backgroundColor: 'white' }}
              />
            );
          }}
        />
      </View>
    )
  }
}

import catalog from './data/catalog.json'; 

class App extends Component {
  render() {
    const data = catalog;
    return (
      <View style={styles.container}>
        <Text h3 style={styles.title}>Skillcase</Text>
        <Feed data={data} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  title: {
    margin: 20,
    marginBottom: 10,
    color: 'white'
  },
  section_title: {
    padding: 20,
    color: 'white',
    backgroundColor: '#1976D2',
  },
  container: {
    flex: 1,
    paddingTop: Expo.Constants.statusBarHeight,
    backgroundColor: '#42A5F5',
  },
});

Expo.registerRootComponent(App);

or expo uri: exp://8v-xvw.outatime.skillcase.exp.direct:80

Thanks !!!

Your example code has many bad practices that are addressed in the documentation. For example, you are recreating and rebinding functions constantly, which is a tax on the CPU and causes unnecessary re-renders of your list items. You should make sure your ListItem component is a PureComponent, and make sure all the props passed to it remain shallow-equal to prevent unnecessary re-renders. It's good practice for the rest of your app as well.

That should help a lot, but I can't guarantee perfect perf on slow devices. How much better do other apps like the web browser work on those devices?

@outaTiME you also shouldn't be calling loadFeed on each render

@lprhodes render method only executes one, btw i update the code using some best practices and still choppy on an iPad 3 with iOS 9 (in iPhone 7 all goes smooth), I keep doing something wrong @sahrens? thanks for your advice.

import React, { Component, PureComponent } from 'react';
import {
  FlatList,
  ListView,
  Text,
  StyleSheet,
  View,
} from 'react-native';
import Expo from 'expo';
import {
  ListItem,
  SearchBar,
} from 'react-native-elements';

class FeedRow extends PureComponent {

  render() {
    const item = this.props.data;
    return (
      <Text>{item.name_group_sp}</Text>
    )
  }

}

class Feed extends Component {

  constructor(props) {
    super(props);
    this.storeListRef = this.storeListRef.bind(this);
  }

  loadFeed() {
    const {data} = this.props;
    const sections = [];
    data.forEach((value, index) => {
      const sectionName = value.name_line_sp;
      const section = sections.find((section) => {
        return section.title === sectionName;
      });
      if (section) {
        section.data.push(value);
      } else {
        sections.push({
          title: sectionName,
          data: [value]
        });
      }
    });
    if (__DEV__) {
      console.log('Sections size', sections.length /*, images.length */);
    }
    return sections[0];
  }

  componentDidMount() {
    this.flatListRef.scrollToOffset({
      animated: false,
      offset: 48
    });
  }

  renderItem(item) {
    return (
      <FeedRow data={item.item} />
    );

  }

  keyExtractor(item) {
    return item.code_group;
  }

  listHeaderComponent() {
    return (
      <SearchBar
        lightTheme
        placeholder='Buscar...' />
    );
  }

  storeListRef(list) {
    this.flatListRef = list;
  }

  render() {
    const feed = this.loadFeed();
    return (
      <View style={{flex: 1}}>
        <Text h5 style={styles.section_title}>{feed.title} ({feed. data.length})</Text>
        <FlatList
          style={{flex: 1}}
          ref={this.storeListRef}
          debug
          // pagingEnabled
          ListHeaderComponent={this.listHeaderComponent}
          data={feed.data} // ~217 records
          keyExtractor={this.keyExtractor}
          renderItem={this.renderItem}
        />
      </View>
    )
  }

}

import catalog from './data/catalog.json';

class App extends Component {

  render() {
    const data = catalog;
    return (
      <View style={styles.container}>
        <Text h3 style={styles.title}>Skillcase</Text>
        <Feed data={data} />
      </View>
    );
  }

}

const styles = StyleSheet.create({
  title: {
    margin: 20,
    marginBottom: 10,
    color: 'white'
  },
  section_title: {
    padding: 20,
    color: 'white',
    backgroundColor: '#1976D2',
  },
  container: {
    flex: 1,
    paddingTop: Expo.Constants.statusBarHeight,
    backgroundColor: '#42A5F5',
  },
});

Expo.registerRootComponent(App);

Better, but setting debug will slow things down a ton. You should also make sure you're running an optimized production build, not a dev / debug build, when evaluating performance. Lastly, you're still creating a new style object every render, and you can use initialScrollPosition instead of calling scrollToOffset in onMount.

@sahrens yup without the debug flag and with optimize production build runs better less choppy, as I said before I'm using expo 17 which uses react-native 0.44, I guess the new version of react-native (0.45) has improvements on the FlatList.

btw, why styles create in every render? in this case the render with the flex styles (in Feed component) are executed only once:

  1. App render
  2. Feed render
  3. Then multiple FeedRow render

Very similar to what @lprhodes told me about the loadFeed on each render, I understand that the Feed render is executed only once, is this correct?

When you say initialScrollPosition you mean initialScrollIndex right? it doesn't work like scrollToOffset i need to hide the ListHeaderComponent (48px of height) when Feed mounts

What about this console warning? im using a PureComponent for renderItem right?

VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"dt":85588,"prevDt":1497296394821,"contentLength":10023}

Thanks !!

That warning is kind of a bug that has been fixed on master, but 85 seconds for your initial mount is crazy slow - you should really dig deeper in your code to figure out what's taking so long. There are many resources out there if you google for react performance, or ask around in the broader React community for help.

And yes, I meant initialScrollIndex. You'll need to implement getItemLayout for it to work, which should account for ListHeaderComponent.

great @sahrens, I am afraid about old hardware like iPad 3 or OS version iOS 9 ... testing on newer devices there is no performance issues, I wanted to know if anyone else was experiencing the same hardware issue ... In a while I'll be ask the community to see if I get to something deeper, thks !!!

Hi mine fixed by giving style={{ backgroundColor: 'white' }} to flat list

So @hramos & @sahrens, what exactly is the best workaround for this issue with FlatList? Running into this with react 0.44.0 using react-native-tab-vew, and it's not really clear what the core team is recommending at this time.

@sjmueller didn't @sahrens say that this is already fixed in master?

@hramos I just reviewed this whole thread again. I may have missed it, but nowhere did I see @sahrens mention that FlatList going invisible is fixed on master. Nor did I see a commit/PR that references this issue, and finally no recommended workaround for fixing the problem.

I do see that @yaronlevi recommends setting lazy={true} on react-native-tab-view or TabNavigator, but that causes jumpy delays even on an iPhone 7 plus.

@knappdev says it works without lazy on 0.44.2 and beyond, so I'll try to upgrade from 0.44.0 and see if I have success.

My work around is to turn removeClippedSubviews on/off depending on whether the flatlist is in use or not.

  constructor (props) {
    super(props)

    this.state = { removeClippedSubviews: false }

    this._disableRemoveClippedSubviews = this._disableRemoveClippedSubviews.bind(this)
    this._onViewableItemsChanged = this._onViewableItemsChanged.bind(this)
    this._renderItem = this._renderItem.bind(this)
  }

  _disableRemoveClippedSubviews () {
    this.setState({ removeClippedSubviews: false })
  }

  _onViewableItemsChanged({ viewableItems, changed }) {
    if (!this.state.removeClippedSubviews) this.setState({ removeClippedSubviews: true })
    if (this._disableRemoveClippedSubviewsTimeout) clearTimeout(this._disableRemoveClippedSubviewsTimeout)
    this._disableRemoveClippedSubviewsTimeout = setTimeout(this._disableRemoveClippedSubviews, 3000)
  }

  render () {    
    const { removeClippedSubviews } = this.state

    return (
      <FlatList
        renderItem={this._renderItem}
        removeClippedSubviews={removeClippedSubviews}
        onViewableItemsChanged={this. _onViewableItemsChanged}
      />
    )
  }

removeClippedSubviews is now off by default for FlatList.

For issues on slow devices, have you tried the RNTester app FlatListExample?

How does FlatListExample perform for you?

removeClippedSubviews is now off by default for FlatList.

Yeah - that's why I turn it on when the actually scrolling. I get fewer random jitters that way.

I then replace (far) out of view cells with empty ones in order to reduce memory overheard further than FlatList does by itself

So, finally, still no way to fix it instead of using removeClippedSubviews={false} even it costs performance so hard?

The fix for me was to disable debugging js remotely for ios simulator

Setting lazy: true seems to have worked for me. Haven't tested perf yet.

I solve this issue by removeClippedSubViews={false}

BEST WORK AROUND
Works on all hardware/simulators. removeClippedSubViews={false} doesn't always work on iPhone 7.

Make sure to force movement in list view.

componentDidMount() {
   requestAnimationFrame(() => {
      // HACK TO RELOAD DATA
      this.refs._list.scrollTo({x: 1, y: 0, animated: false})
    });
}

Example of reference.

 <ListView
     ref="_list"
     dataSource={this.state.dataSource}
     renderRow={(data, sectionId, rowId) => <Row
        rowId={rowId}
        selectedRow={this.state.selectedRow}
        onPressRow={this._pressRow.bind(this)}
        {...data}/>}
     renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator} />}
     removeClippedSubViews={false}
     initialListSize={SortedBrandList.count}/>

The problem is that it may have already been mounted

On 24 Aug 2017, at 3:44 pm, Peter Suwara notifications@github.com wrote:

WORK AROUND
Works on all hardware/simulators.

'''componentDidMount() {
requestAnimationFrame(() => {
// HACK TO RELOAD DATA
this.refs._list.scrollTo({x: 1, y: 0, animated: false})
});
}'''

'''
ref="_list"
dataSource={this.state.dataSource}
renderRow={(data, sectionId, rowId) => }
renderSeparator={(sectionId, rowId) => }
removeClippedSubViews={false}
initialListSize={SortedBrandList.count}
/>'''


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

I solved this issue, at least on the simulator, with:

<ListView removeClippedSubViews={false} .... />

My guess here is that the routine which manages clippedSubviews is not checking for bounds properly when the ListView isn't currently on screen. This happens pretty often with any ListView component that renders offscreen and then moves on screen. This shouldn't be too hard of a bug to fix.

I'm pretty sure this function is the issue: https://github.com/facebook/react-native/blob/master/React/Views/RCTView.m#L321-L369

My guess is that the coordinate space it's measuring is incorrect some where. Looking at it now.

Still no fix for this?

@petersuwara: what you proposed work. Thank you. @MattFoley : When will your fix available for download?

Not specific just To FlatList, seems to be a problem in loading it in a ListView as well.
I found a workaround by using a slight delay in setting the datasource.

When the data is loaded from a remote source in the componentWillMount method, the following code is run.
Seems to work :

setTimeout(function(){
       this.updateDataSource(array,newArray)
},30)

updateDataSource = (comments,dataSource) =>{
        this.setState({
            allComments:comments,
            dataSource: this.state.dataSource.cloneWithRows(dataSource),
            isLoading:false

        })
}

And during the slight delay you can always show a loading screen. It is so short that it is not even visible.

I've found @nathvarun 's method to work. There seems to be some race condition between the component mounting thread and the component's update data source thread. If this is indeed the case I am confused why this would happen since the two threads should play sequentially nice with each other.

This occurs in components that are already mounted though such as screens assigned to tabs in react-navigation but are more currently selected - so I'm unsure how an amend to componentWillMount could fix this.

My PR above seems to have fixed the problem, but I haven’t had any luck getting it merged in. Would someone else like to chime in on that PR?

@MattFoley Whether or not getting it merged. I'm using your code now. And it really fix my issue.

Configuring the TabNavigator of react-navigation to be lazy: true also works... But would be great to see this fixed by react-native.

If you're using SectionList or Flatlist, just use the ref = {(ref)=> this.sectionList = ref }
and in your code this.sectionList.recordInteraction() - should render your view

@MattFoley's proposed fix has been merged, and is now part of the 0.50 release: https://github.com/facebook/react-native/commit/03ae65bc25185fe6d7f62e66f5575ced5c3e8378

I just updated and I'm still seeing this issue on <ListView />. Am I doing something wrong? Here's my package.json:

  "dependencies": {
    "native-base": "^2.3.1",
    "react": "^16.0.0",
    "react-native": "^0.50.0-rc.1",
    "react-native-linear-gradient": "^2.3.0",
    "react-native-modal": "^4.1.0",
    "react-native-simple-store": "^1.3.0",
    "react-navigation": "^1.0.0-beta.11"
  },

i think ListView is deprecated by now. its better try with FlatList and see whats up.

I believe we're still seeing this on 0.50.3. Can anyone else confirm?

I fixed this issue by switching to FlatList.
On Fri, Nov 17, 2017 at 8:39 AM Colin Ramsay notifications@github.com
wrote:

I believe we're still seeing this on 0.50.3. Can anyone else confirm?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/facebook/react-native/issues/1831#issuecomment-345294840,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAZnGZ9oz7uKXx-wz3KFg-FSIzejAfM6ks5s3bavgaJpZM4FP1nt
.

Ah, we're already _using_ FlatList.

This is still a problem with 0.50.3. removeClippedSubviews don't seem to have any effect. Can we reopen this please?

We still have an issue in 0.50.3. Please reopen this one.

Sorry to bother again, but can we reopen this please? This is a pressing issue.

Ran into this using the latest Expo React Native SDK. It only happens to the third ListView in a TabNavigator. If I swap the 2nd and 3rd, then the new 3rd page will be affected. removeClippedSubviews did fix it in my case though.

requestAnimationFrame(() => { this.listview.scrollTo({y: 1}); }); worked for me.

using this.listView.scrollToEnd() didn't work, so I had to manually calculate the coordinates of the end of the list to then pass to scrollTo()

For flatlist this could be caused by updating the same array. FlatList checks if data is different. So make use of immutable arrays

removeClippedSubviews={false} fixes the bug for my case.

<ListView
            data={this.state.mockData}
            renderRow={(rowData) => this.renderRow(rowData)}
            removeClippedSubviews={false}
 />

@edmengel is right. I was working on a project and had exactly the same bug.

Before scrolling:
screen shot 2018-03-10 at 05 44 59

After scrolling:
screen shot 2018-03-10 at 05 48 29

React-native-router-flux@^4.0.0-beta.27 is used for the navigation and App.js returns this. My search page contains a listview and it had this bug. It is on the third tab.

<Provider store={store}>
        <View style={{flex: 1}}>
            <Router hideNavBar={true}>
                <Scene key="modal" modal>
                    {/* Tab Container */}
                    <Scene key="tabbar" tabs={true} tabBarPosition="bottom" tabBarStyle={{borderTopColor:'black', borderTopWidth:1,backgroundColor:'white'}}>
                      {/*Tabs */}
                        <Scene key="NewPage" component={NewPage} title="NewPage" icon={TabIcon}  hideNavBar={true} />
                        <Scene key="NewPage2" component={NewPage2} title="Newpage2" icon={TabIcon}  hideNavBar={true} />
                        <Scene key="SearchPage" component={SearchPage} title="Search"  hideNavBar={true} />
                    </Scene>
                </Scene>
            </Router>
        </View>
</Provider>

When I changed search page(which contains a listview) from third tab to second tab, listview worked fine.

Found a working Hack for this.

render() {
       setTimeout(() => {
            this.sectionList && this.sectionList.recordInteraction(); 
        }, 50);
        return (
            <SectionList ref={(view) => { this.sectionList = view }} />
        )
}

<Flatlist style={{ flex: 1 }}/>
This trick work for me.

Same issue on 0.55.4 with both ScrollView and ListView. Adding removeClippedSubviews={false} works.

Unfortunately, in our case, it is not an option, since it is required to avoid missing images on Android https://github.com/facebook/react-native/issues/13600#issuecomment-315629140

Scrolling back and forth on render only happens after navigation so, it looks like transitioning to an initially blank screen.

Flexing the view didn't help either.

Any other suggestions?

I found it to only happen after certain scroll position...
https://streamable.com/l5arv

Using chrome-devtools to inspect the view I can see that all the subviews are clipped.

Still stuck, @hramos please re-open.

Still having a problem with such a simple function? This is why we moved away from React Native back to straight Native code.

If it’s that simple, please lend out a hand in order to fix it @petersuwara

This issue has been closed for a while. If this is affecting you, _please open a new issue_ and provide as much detail as possible. I'm locking this issue as the original problem reported here was fixed, and it's not clear each person who has commented afterwards is facing the exact same problem.

We use FlatList _extensively_ at Facebook, and no complaints of this sort have surfaced internally. It may be possible this type of issue occurs with a particular navigation library, for example. Opening a new issue with more information about your problem, with a clear list of steps to repro, or ideally a small project, would be of great help.

Was this page helpful?
0 / 5 - 0 ratings