FlatList re-render all data when new data is added

扶醉桌前 提交于 2020-03-04 19:37:52

问题


I test FlatList. Here is what I want to do:
1. Get 10 initial data with componentDidmount
2. Scroll down to get 10 more data

App.js

import React from 'react';
import {
  View,
  SafeAreaView,
  Text,
  FlatList,
} from 'react-native';
import throttle from 'lodash.throttle';
import { tmpPosts } from './src/data/tmpdata';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.page = 0;
    this.state = {
      posts: [],
    };
    this.getMoreDataThrottled = throttle(this.getMoreData, 3000);
  }

  componentDidMount() {
    console.log('comoponentDidMount')
    this.getMoreDataThrottled();
  }

  componentWillUnmount() {
    this.getMoreDataThrottled.cancel();
  }

  getMoreData = () => {
    console.log('start getMoreData')
    const tmpList = []
    for (let i = 0; i < 10; i++ ) {
      tmpList.push(tmpPosts[this.page])
      this.page += 1;
    }

    this.setState(prevState => ({
      posts: prevState.posts.concat(tmpList)
    }));
  }

  renderPost = ({item}) => {
    console.log(item.id)
    return (
      <View style={{height: 200}}>
        <Text>{item.id}</Text>
      </View>
    );
  };

  render() {
    return (
      <SafeAreaView>
        <FlatList
          data={this.state.posts}
          renderItem={this.renderPost}
          keyExtractor={post => String(post.id)}
          initialNumToRender={10}
          onEndReachedThreshold={0.01}
          onEndReached={this.getMoreDataThrottled}
        />
      </SafeAreaView>
    );
  }
}

export default App;

tmpdata.js

let num = 0;
export const tmpPosts = [
  {id: String(num += 1)},
  {id: String(num += 1)},
  .
  .
  .
]

I implemented what I want, but the rendering occurs much.
Here is console.log

comoponentDidMount
start getMoreData
1
2
3
.
.
.
8
9
10
1
2
3
.
.
.
8
9
10
start getMoreData
1
2
3
.
.
.
8
9
10
1
2
3
.
.
.
18
19
20
start getMoreData
1
2
3
.
.
.
18
19
20
1
2
3
.
.
.
28
29
30

It seems the log means:
1. Re-rendering occurs twice every time.
2. FlatList rendering num increases, because it renders old data too.

I check similar issue, and it seems regular behaviour of FlatList.
FlatList renderItem is called multiple times
FlatList calling twice
https://github.com/facebook/react-native/issues/14528

However I'm afraid that the app will be slow and crashed when the data reaches 100 more.
What is the best solution for FlatList performance?
shouldComponentUpdate prevent unnecessary re-rendering such as old data that has already been rendered?


回答1:


Update

Sorry, my response was incomplete. I reproduced your problem and fixed it by moving your "Post" component in a separate file. This post extend pureComponent, so it'll not re render if it's props doesn't changed.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.page = 0;
    this.state = {
      posts: []
    };
    this.getMoreDataThrottled = throttle(this.getMoreData, 3000);
  }

  componentDidMount() {
    console.log("comoponentDidMount");
    this.getMoreData();
  }

  componentWillUnmount() {
    this.getMoreDataThrottled.cancel();
  }

  getMoreData = () => {
    console.log("start getMoreData");
    const tmpList = [];
    for (let i = 0; i < 10; i++) {
      tmpList.push(tmpPosts[this.page]);
      this.page += 1;
    }

    this.setState(prevState => ({
      posts: prevState.posts.concat(tmpList)
    }));
  };

  renderPost = ({ item }) => {
    return <Post item={item} />;
  };

  render() {
    return (
      <SafeAreaView>
        <FlatList
          data={this.state.posts}
          renderItem={this.renderPost}
          keyExtractor={post => String(post.id)}
          initialNumToRender={10}
          onEndReachedThreshold={0.5}
          onEndReached={this.getMoreData}
        />
      </SafeAreaView>
    );
  }
}

/*
  In a separate file
*/
class Post extends React.PureComponent {
  render() {
    const { item } = this.props;
    console.log(item.id);
    return (
      <View key={item.id} style={{ height: 200 }}>
        <Text>{item.id}</Text>
      </View>
    );
  }
}

// Same but in functional (my preference)
const _PostItem =({ item }) => {
  console.log(item.id);
  return (
    <View key={item.id} style={{ height: 200 }}>
      <Text>{item.id}</Text>
    </View>
  );
};

export const PostFunctionalWay = React.memo(_PostItem);

export default App;

Original answer

I think you forgot to add the prop "extraData" in your flatlist :

<FlatList
          data={this.state.posts}
          extraData={this.state.posts}
          renderItem={this.renderPost}
          keyExtractor={post => String(post.id)}
          initialNumToRender={10}
          onEndReachedThreshold={0.01}
          onEndReached={this.getMoreDataThrottled}
/>

https://facebook.github.io/react-native/docs/flatlist#extradata

PS : maybe with extending PureComponent

PS2 : you need that your rendering items(renderPost) have a prop "id" with a unique key

 renderPost = ({item}) => {
    console.log(item.id)
    return (
      <View id={item.id} style={{height: 200}}>
        <Text>{item.id}</Text>
      </View>
    );
  };



回答2:


Uninstall and go native. RN is full of bugs.

https://softwareengineeringdaily.com/2018/09/24/show-summary-react-native-at-airbnb/



来源:https://stackoverflow.com/questions/57381341/flatlist-re-render-all-data-when-new-data-is-added

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!