Search code examples
javascriptreact-nativereact-native-flatlist

Prevent FlatList from re renders when numColumns > 1


I use FlatList in a react native project to display a image grid view, which represents an upload queue.

<FlatList
  extraData={this.state.images}
  data={this.state.images}
  renderItem={this.renderItem}
  keyExtractor={image => {
   return image.id;
  }}
/>



renderItem = ({item, index}) => {

    return (
      <TouchableOpacity
        id={item.id}
        delayLongPress={500}
        //onLongPress={() => this.props.onLongPressImage(item)}
      >
        <SingleImage
          onFinishUpload={this.props.onFinishUpload}
          index={index}
          item={item}
          title={this.props.title}
        />
      </TouchableOpacity>
    );
};

As soon as i set numColumns > 1 every time a image is added or removed the other images re rendered and componendDidMount of the SingeImage class fires.

That's a problem, because i start the upload of the certain image in the componendDidMount() and every time a upload is successful the image is removed from the FlatList. All the other images uploading are remounted and restart their upload process.

If i just display a single row and numColumns is not set, every element of the list is just rendered once and everything works fine.

Do i miss something or how can i prevent the list from re render every item the data array is modified?


Solution

  • First, it's important to keep in mind difference between re-render and remount. It's perfectly fine components (SingleImage in your case) to re-render, but NOT fine for them to remount because in that case components are destroyed and re-created and, of course, lose state

    Now to the point: FlatList extends VirtualizedList

    Virtualization massively improves memory consumption and performance of large lists by maintaining a finite render window of active items and replacing all items outside of the render window with appropriately sized blank space

    In a nutshell, FlatList does not instantiate and keep alive all the instances of component you create to render. Imagine a list with 10000 items, it will NOT create 10000 instances of SingleImage and keep them alive. Instead, it will create only a few of them (usually as many as fit on the screen) and, as you scroll, the result of a previous render will be discarded and these "slots" will be reused for items with other data. This is the essence of virtualization, which allows displaying of infinitely large lists by drawing only a small portion of data

    "Drawing" is the key here (that's why the prop is called renderItem, not Item). It will just draw something on the "slot" and forget about it. This is the reason you can't render stateful components in FlatList's renderItem. Internal state of these items is not preserved on re-renders

    The correct way to do this would be handling image uploading outside of FlatList and only using FlatList to draw the UI with stateless components. As a cheaper alternative, if you really want to keep logic and presentation inside one SingleImage component, you can use this.state.images.map instead of FlatList (like you would do it in React web) to make sure all items are rendered at the same time and preserve their identity across re-renders

    The reason why it doesn't bug out without numColumns prop set is, I suppose, due to the fact that numColumns triggers some additional re-renders, but having additional re-renders is perfectly fine and is not a root of a problem. Basically, without numColumns you are just getting lucky because there isn't enough of items in the list for virtualization to take place. If you added some more items, you would observe the same effect even without numColumns