Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FlatList] Sometimes FlatList rows are rendered, but not displayed until scroll. RN 0.43 #13316

Closed
joonhocho opened this issue Apr 5, 2017 · 78 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@joonhocho
Copy link
Contributor

Description

When using FlatList, rows are not displayed though renderItem is called.
Rows appear immediately when scroll is triggered on the list.

Reproduction Steps and Sample Code

export default class List extends Component {
  renderItem({item}) {
    return (
      <Text>{item}</Text>
    );
  }

  render() {
    return (
      <FlatList
        style={{
          flex: 1,
          backgroundColor: 'white',
        }}
        data={[1, 2]}
        renderItem={this.renderItem}
      />
    );
  }
}

Solution

FlatList should always display its rendered items.

Additional Information

  • React Native version: 0.43.0
  • Platform: iOS
  • Development Operating System: MacOS
  • Dev tools: Xcode
@joonhocho joonhocho changed the title [FlatList] Sometimes rows are rendered, but not displayed until scroll. [FlatList] Sometimes FlatList rows are rendered, but not displayed until scroll. RN 0.43 Apr 5, 2017
@joonhocho
Copy link
Contributor Author

Related #13202

@joshjhargreaves
Copy link
Contributor

@joonhocho could you create a gif of this in action? I can't reproduce with your example.

@joshjhargreaves
Copy link
Contributor

Are you experiencing this issue in a standalone project and/or are you able to reproduce in a standalone project if not?

@Thomas101
Copy link
Contributor

We're also seeing this issue. Looks like it could be something to do with the FlatList being in a navigator on iOS. Managed to reproduce it in a standalone project. You'll have to excuse the code it's a bit rough.

react-native init and add the following code as your index.ios.js. Once launched, tap on A. Tap remove. Tap back. Then you'll need to scroll to get FlatList to re-render.

We found this on RN0.43.1 iOS simulator, iPad but not android. Seems to work as expected on android

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, FlatList, TouchableOpacity, Navigator } from 'react-native';

let data = [ { id: 'a' }, { id: 'b' }, { id: 'c' } ]
let listeners = []

class List extends Component {
  constructor (props) {
    super(props)
    this.state = { data: Array.from(data) }
  }

  componentDidMount () {
    listeners.push(() => { this.setState({ data: data }) })
  }

  render () {
    return (
      <FlatList
        data={this.state.data}
        keyExtractor={(item) => item.id}
        renderItem={({item}) => (
          <TouchableOpacity onPress={() => this.props.navigator.push({ id: 'r2', item: item })}>
            <View>
              <Text>{item.id}</Text>
              <Text>{item.id}</Text>
              <Text>{item.id}</Text>
              <Text>{item.id}</Text>
            </View>
          </TouchableOpacity>
        )}
      />
    )
  }
}

export default class AwesomeProject extends Component {

  renderScene (route, navigator) {
    if (route.id === 'r1') {
      return (<List navigator={navigator} />)
    } else if (route.id === 'r2') {
      return (
        <View>
          <Text>{route.item.id}</Text>
          <TouchableOpacity onPress={() => {
            data = [data[0], data[2]]
            listeners.forEach((l) => { l() })
          }}>
            <Text>Remove</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => navigator.pop()}>
            <Text>Back</Text>
          </TouchableOpacity>
        </View>
      )
    }
  }

  render() {
    return (
      <View style={[StyleSheet.absoluteFill, { paddingTop: 50 }]}>
        <Navigator initialRoute={{ id: 'r1' }} renderScene={this.renderScene.bind(this)} />
      </View>
    );
  }
}

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

@sahrens
Copy link
Contributor

sahrens commented Apr 5, 2017

Does setting removeClippedSubviews={false} fix the issue? Sounds like the issue might be related to the navigator which would explain why we don't see this problem at Facebook. cc @shergin

@joonhocho
Copy link
Contributor Author

@Thomas101 @sahrens I am using react-navigation for your information.

@raffij
Copy link

raffij commented Apr 5, 2017

@sahrens removeClippedSubviews={false} works for us for now.

We are using Navigator for a tab based app.

  1. Navigate to the flatlist scene. List shows.
  2. Navigate to another tab.
  3. Make a change which affects the flatlist scene.
  4. Navigate to the already mounted flatlist scene.
  5. Flatlist shows as empty.
  6. Touch component and the items are made visible.

@joonhocho
Copy link
Contributor Author

@raffij @sahrens It also fixed the issue for me as well.

@Thomas101
Copy link
Contributor

removeClippedSubviews={false} works for us too. I noticed in the VirtualizedList docs there's some discussion around setting this as the default, could I +1 that as I imagine quite a few people will come up against this?

@sahrens
Copy link
Contributor

sahrens commented Apr 6, 2017

I'm running an experiment in production soon to see the effect on scroll perf. If the hit isn't too bad, we'll turn it off by default.

@whoyawn
Copy link

whoyawn commented Apr 7, 2017

Running into a similar problem here. It isn't exactly scrolling that's my problem though. What I have is a horizontal FlatList with paging enabled. I have a function that adjusts each item of the list to the width of the screen. What I'm doing right now is using redux and async storage to persist all my items, but the items do not render until I hit a button in my view to add another item in the list. When I set removeClippedSubviews={false} the view renders, but only part of it. I suspect it has something to do with the render prop, because when I render just a TextView I see the display, but not when I render my full custom component. I'll post my code and a video tomorrow.

@joenoon
Copy link
Contributor

joenoon commented Apr 9, 2017

I had a possibly related issue with a ListView (RN 0.43.2) that seems to be solved by the removeClippedSubviews={false} suggestion.

In my case this ListView was inside an Animated.View with a translateY style (sliding down). When the animation ended the ListView would have variable space above the first row (as if there was some contentInset padding). Just barely scrolling the list would snap everything back to normal and the first row would be correctly at the top.

Just noting it here in case it might provide a hint.

@sahrens
Copy link
Contributor

sahrens commented Apr 13, 2017

I'm putting in a diff to disable removeClippedSubviews by default in VirtualizedList, so it will be opt-in and hopefully won't bite people like this in the future.

@joonhocho
Copy link
Contributor Author

joonhocho commented Apr 13, 2017

@sahrens Could you explain why removeClippedSubviews fixes the issue? Is setting removeClippedSubviews as false an optimal solution or is it just a temporary quick fix?

Because I am using other RN libraries like react-native-sortable-listview that suffers from the similar issue (rows not appearing until initial scroll) even though it uses ListView, not FlatView. Thus, I guess it's not only limited to FlatList, but also affect other types of scroll views as well. When I manually set removeClippedSubviews={false}, it fixes the issue, but also causes some unwanted side effects.

@Noitidart
Copy link

Could you explain why removeClippedSubviews fixes the issue?

I am wodering this question as well. I am using react-navigation as well. Setting removeClippedSubviews to false also fixed this for me. I'm not sure of it's implications (reduced perf?).

Maxwell2022 pushed a commit to Maxwell2022/react-native that referenced this issue Apr 19, 2017
Summary:
It's just causing problems (e.g. when combined with transform animations like those used
in some navigators) and hopefully it's not necessary with JS-side windowing. If people need the
perf, they can turn it on themselves.

Should fix facebook#13316 and related issues.

Reviewed By: achen1

Differential Revision: D4884147

fbshipit-source-id: 95c82448581076c0d0b2c80b1cd80cc294898174
@npomfret
Copy link
Contributor

I'm getting this too in RN 0.43.3. Why is this closed?

@joshjhargreaves
Copy link
Contributor

I think the conclusion is that setting removeClippedSubviews={false} seems to fix these issues? Have you tried this yet?

@npomfret
Copy link
Contributor

Yeah, i just figured that out, cheers. Much better. But it's still a bug, no?

@sahrens
Copy link
Contributor

sahrens commented Apr 28, 2017

removeClippedSubviews is now off by default in master.

@vjeranc
Copy link
Contributor

vjeranc commented May 4, 2017

This also breaks components like MapView in react-native-maps (the map gets frozen on some area until swipe or some other gesture event). So, this is not just an issue with react-native components.

Adding removeClippedSubviews={false} does not work there (even when going to the deepest React.Component.

@whoyawn
Copy link

whoyawn commented May 6, 2017

Got something really weird.

I'm trying to scroll to the end of my horizontal pager I made out of the Flatlist with this

componentDidMount() {
    this._listRef.scrollToEnd({ animated: false });
  }

Here's my Flatlist:

<FlatList
          ref={(flatList) => { this._listRef = flatList; }}
          data={this.props.log}
          onLayout={this.adjustPageSize}
          renderItem={this.renderPage}
          showsHorizontalScrollIndicator={false}
          getItemLayout={(data, index) => {
            const width = Dimensions.get('window').width;
            return { length: width, offset: width * index, index };
          }}
          removeClippedSubviews
          horizontal
          pagingEnabled
          directionalLockEnabled
        />

When I make it like this, my list does not render until I scroll, but when I change getItemLayout to this:

 getItemLayout={(data, index) => {
            const width = Dimensions.get('window').width;
            return { length: 0, offset: width * index, index };
          }}

with the length attribute as 0, my list renders perfectly fine, and it scrolls to the end correctly in componentDidMount

@sahrens
Copy link
Contributor

sahrens commented May 6, 2017

@HuyAnhh: that is indeed quite weird. Does it work without the length hack and without removeClippedSubviews?

thotegowda pushed a commit to thotegowda/react-native that referenced this issue May 7, 2017
Summary:
It's just causing problems (e.g. when combined with transform animations like those used
in some navigators) and hopefully it's not necessary with JS-side windowing. If people need the
perf, they can turn it on themselves.

Should fix facebook#13316 and related issues.

Reviewed By: achen1

Differential Revision: D4884147

fbshipit-source-id: 95c82448581076c0d0b2c80b1cd80cc294898174
@jslok
Copy link

jslok commented Nov 30, 2017

#1831
According to that, the fix was merged in and available for RN 0.50. If you do not want to or cannot upgrade RN yet, you can just change the line as shown in this file.
03ae65b

On my app, this fix solves the issue and I can still use removeClippedSubviews={true}.

@facuacostag
Copy link

@douglasjunior Sorry to bother, but how did you implement the Activity Indicator while scrolling the FlatList?

@douglasjunior
Copy link

When fetching the data, render an ActivityIndicator as ListFooterComponent. Example here.

@almostintuitive
Copy link

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

@blake-simpson
Copy link

Inspired by the comments in this thread from Kiarash-Z and douglasjunior, my fix to this solution was basically:

ListFooterComponent={() => {
  return <View style={{backgroundColor: 'transparent', height: 1}} />
}}

I could not use a ActivityIndicator but it seems RN needs new content to be rendered, even an "invisible" view (Note, with height of zero the bug still existed).

@companyAcc
Copy link

React Native FlatList shows some blank in the quickly sliding. What is the solution?

@almostintuitive
Copy link

we've migrated over to use https://github.com/Flipkart/recyclerlistview

@rogerkerse
Copy link
Contributor

@BlakeSimpson this is good, but again it is not viable solution in the long run because it is basically a hack.

Why is this closed again, if this is not resolved 👎

@blake-simpson
Copy link

@rogerkerse I totally agree. This is a hack and a real solution would be preferable.

We've recently upgraded to RN 0.52 though and the issue seems to be resolved, so I am removing my hack. Perhaps you can upgrade your React Native version also or just use the hack temporarily until you can?

@Laurensdc
Copy link

Laurensdc commented Feb 13, 2018

Super weird, when I edited getItemLayout to not match my actual item component, it rendered immediately again for me...

getItemLayout={(data, index) => (
    {
        length: itemWidth - 1, // why does this work
        offset: itemWidth * index,
        index
    }
)}

Edit: Or not.

@Laurensdc
Copy link

I might have resolved it by only passing initialScrollIndex if the items would actually go off screen.

initialScrollIndex={sortedEntries.length > 5 ? sortedEntries.length - 1 : 0}

Feels like this kind of logic should not be necessary.

@DOHere
Copy link

DOHere commented Mar 7, 2018

@bensheldon that solved it for me, thanks!
setting length in getItemLayout to window width worked for me
I had set it before to item length

@esutton
Copy link

esutton commented Mar 12, 2018

After refactoring to use FlatList I must go back to ListView. :-(

None of these solutions work for me.

ListView just works.

		"react": "^16.2.0",
		"react-native": "^0.53.3",
		"react-native-router-flux": "4.0.0-beta.27",

@DOHere
Copy link

DOHere commented Mar 12, 2018

@esutton can you show your FlatList, getItemLayout and css used in the FlatList?

@esutton
Copy link

esutton commented Mar 12, 2018

@DOHere I am not using the getItemLayout option.

  renderRow = ({ item, index }) => {
    const menuItem = item;
    return (
      <MenuItemRow
        menuItem={menuItem}
        onPressMenuItem={this.onPressMenuItem}
        {...this.props}
      />
    );
  }

  render() {
    const { themeStyle } = this.props.state;
    const connecting = (this.state.animating || this.state.connecting)
      ? (<View style={[styles.row, themeStyle.view]}>
        <Text style={[themeStyle.textPrimary, { fontWeight: 'bold' }]}>
          {this.state.progressMessage}
        </Text>
        <ActivityIndicator
          animating={this.state.animating}
          style={[
            styles.centering,
            {
              backgroundColor: themeStyle.textPrimary.backgroundColor,
            },
          ]}
          color={themeStyle.textPrimary.color}
          size="large"
        />
      </View>)
      : null;

    return (
      <View style={styles.listContainer}>
        {connecting}
        <FlatList
          ref={(ref) => { this.listView = ref; }}
          style={themeStyle.view}
          ItemSeparatorComponent={this.renderSeparator}
          data={this.state.dataSource}
          renderItem={this.renderRow}
          refreshing={this.state.animating}
          onRefresh={this.onTrackerScan}
          automaticallyAdjustContentInsets={false}
          keyboardDismissMode="on-drag"
          keyboardShouldPersistTaps="always"
          enableEmptySections
          keyExtractor={this._keyExtractor}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  centering: {
    alignItems: 'center',
    justifyContent: 'center',
    padding: 8,
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  listContainer: {
    flex: 1,
  },
  list: {
    marginTop: 0,
    backgroundColor: '#eeeeee',
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 8,
    borderWidth: 0,
    borderColor: '#DDDDDD',
    justifyContent: 'space-between',
  },
  rowSeparator: {
    backgroundColor: 'rgba(0, 0, 0, 0.1)',
    height: 1,
    marginLeft: 4,
  },
  rowSeparatorHide: {
    opacity: 0.0,
  },
  separator: {
    backgroundColor: 'rgba(0, 0, 0, 0.1)',
    height: StyleSheet.hairlineWidth,
    marginVertical: 10,
  },
  separatorHorizontal: {
    backgroundColor: 'rgba(0, 0, 0, 0.1)',
    height: StyleSheet.hairlineWidth,
    marginHorizontal: 10,
  },
});

@DOHere
Copy link

DOHere commented Mar 12, 2018

@esutton I suggest you set the height of rows somewhere in css
and also use getItemLayout. Try this:

  import { Dimensions } from "react-native";
  screenHeight = Dimensions.get("window").height;

  getItemLayout={(data, index) => ({
          length: screenHeight,
          offset: index * <row-height> + ( <row-height> - screenHeight ) / 2,
          index,
        })}

@esutton
Copy link

esutton commented Mar 12, 2018

@DOHere Thank you very much for the tip.

I plan to stay with ListView as long as I can. FlatList has wasted way too much of my time.

After my experience of migrating multiple components to FlatList, I have had to roll back to ListView several times because of rendering not working properly as ListView did.

I will keep your advice in mind when I am forced to upgrade to FlatList.

@roshangm1
Copy link
Contributor

Still with the FlatList in React Native 0.54. It started happening all of a sudden and it's actually annoying.

@vdlindenmark
Copy link

vdlindenmark commented Mar 13, 2018

Please reopen, same issue in RN 0.54 for me... Only Android btw.

@ArturGr
Copy link

ArturGr commented Apr 25, 2018

Found a working Hack for this.

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

@wmonecke
Copy link

wmonecke commented Apr 28, 2018

I dont encounter this error when I install in debugMode. However once I do react-native run-android --variant=release I get this bug.

In my case the rows do render but kind of "not fully". It feels like because the debug variant is slower, it finishes rendering each item. However the release variant is too fast for the items to display properly. The color of the items are sluggish and blend with the background color.

The bug wont happen on iOS.

I have no clue on what to do to fix this. It is really frustrating.

"react": "16.0.0-alpha.12", "react-native": "0.47.1",

@wenkangzhou
Copy link

Finally,the only way for me is back to ListView.

@pyaesone17
Copy link

pyaesone17 commented May 16, 2018

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

@wenkangzhou
Copy link

Adding removeClippedSubviews = {false}works, but it reducing your rendering performance,if you hava a largelist of image,maybe 500 items can lead to crash.

@machester4
Copy link

had the same problem, the problem is that the data source is a JSON and therefore will be passed to the reference and not as a value, by modifying that object from any of its references automatically 'Flatlist' will have these new data and for more if we use setState to try to update the flatList, we will not see the changes until we move because Flatlist will already have that data. It's a bit confusing but here I give you a little 'solution'.

   // We break the reference by making it immutable 
   let playlistInitial =  JSON.stringify(store.getState().playlist);

    if (playlistInitial != "") {
      this.setState({ playlistInitial });
      AudioController.init(JSON.parse(playlistInitial), 0, this.onChangeStatus);
    }

 <FlatList
     data={this.state.playlist}
     initialNumToRender={5}
     style={{ flex: 1 }}
     horizontal={true}
     showsHorizontalScrollIndicator={false}
     keyExtractor={item => item["key"]}
     renderItem={({ item }) => <RowStationHome data={item} />}
 />

@facebook facebook locked as resolved and limited conversation to collaborators May 24, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests