防止FlatList重新呈现导致性能问题

时间:2020-06-01 11:59:23

标签: react-native-flatlist react-virtualized rerender conditional-rendering

我正在使用Hooks和ES6构建一个React Native应用。

主屏幕从API提取一些数据并将其显示为图像库。 主屏幕是父组件,其中包括幻灯片库和子目录。

1)幻灯片放映-图像库自动播放,在每次图像迭代(每3秒)上运行一些操作(以防用户点击时获取URL)-这会导致整个父组件及其子组件的重新渲染,但是按预期每3秒渲染一次

2)平面列表-只是一个简单的平面列表,需要一个图像数组-当首先加载父组件时,该应该只渲染一次

问题

每3秒钟在幻灯片放映中显示一个新图像,运行一个函数为显示的图像获取一些道具,重新渲染父组件,但是由于第二个图库中的Flatlist不需要再次运行,图片数组与最初加载的图片相同(不变)

这是我的父母密码

const getTopTenMovies = (state) => state.moviescreen.popularmovies    //fetching data from Redux
const upcomingMovies = (state) => state.moviescreen.upcomingmovies

const MoviesScreen = (props) => {
  const topTenMovies = useSelector(getTopTenMovies);
  const [imageIndex, setImageIndex] = useState("");

  /* useEffect below runs every time imageIndex changes (a new image is displayed every 3 secs) */
  useEffect(() => {  
      setCarouselMovieId(selectedImageItem.id);
      setCarouselMovieTitle(selectedImageItem.title);        
  }, [imageIndex]);

  /* user clicks on an image and is taken to another screen
  const fetchMovieHandler = async (movieId, movieTitle) => {    
      props.navigation.navigate({
        routeName: "MovieDetail",
        params: {
          assetId: movieId,
          assetName: movieTitle,
        },
      });    
  };

  return (
    <ImageGallery
      data={topTenMovies}
      currentImage=setImageIndex(index)
      ...
    />

    <View style={styles.containerRow}>
          <FlatList
            horizontal={true}
            data={upcomingMovies}
            initialNumToRender={5}
            windowSize={10}
            removeClippedSubviews={true}
            keyExtractor={(item) => item.id.toString()}
            renderItem={(itemData) => (
              <HomeItem
                id={itemData.item.id}
                poster={itemData.item.poster_path}                
                onAssetSelection={fetchMovieHandler}
              />
            )}
          />
        </View>
      </View>
  )
}

该组件在Flatlist中呈现单个图像,并将参数(movieId,movieTitle)传递给fetchMovieHandler函数。

下面的HomeItem代码...

class HomeItem extends React.PureComponent {
  render() {
    const assetHandler = async () => {
      this.props.onAssetSelection(this.props.id, this.props.title);
    };
    console.log("HomeItem is called");
    return (
      <TouchableOpacity onPress={assetHandler}>
          <View>            
              <Image
                key={this.props.id}
                source={{
                  uri: `https://*******${this.props.poster}`,
                }}
              />            
          </View>   
      </TouchableOpacity>
    );
  }
}

export default React.memo(HomeItem);

每隔3秒就会调用一次HomeItem,我会看到10条日志条目(每个图像和渲染一个)-即将上映的电影有10张图像。从UI来看一切正常,因为图像似乎没有变化,可能是因为我已将HomeItem定义为PureComponent并使用React.memo(HomeItem),但是由于出现以下错误,这会导致性能问题:

VirtualizedList:您的列表很大,更新速度很慢-确保您的renderItem函数呈现遵循React性能最佳实践的组件,例如PureComponent,shouldComponentUpdate等

我试图将Flatlist包含在HomeItem组件中,但问题仍然存在。

2 个答案:

答案 0 :(得分:1)

经过进一步的研究,并感谢@landorid的建议,我发现我需要控制FlatList的renderItem,方法是在具有useCallback钩子并依赖于我的数据源的单独函数中声明它。

试图将Flatlist包含在单独的PureComponent中,但重新渲染一直在发生。

理想的解决方案是更改此代码

renderItem={(itemData) => (
  <HomeItem
     id={itemData.item.id}
     poster={itemData.item.poster_path}                
     onAssetSelection={fetchMovieHandler}
  />
)}

像这样

.....
  const renderRow = ({ itemData }) => {
    return (
      <HomeItem
        id={itemData.item.id}
        poster={itemData.item.poster_path}
        title={itemData.item.title}
        onAssetSelection={fetchMovieHandler}
      />
    );
  }; 
  keyExtractor = (item) => item.id.toString();

  return (

<FlatList
     horizontal={true}
     data={upcomingMovies}
     initialNumToRender={5}
     windowSize={10}
     removeClippedSubviews={true}
     keyExtractor={keyExtractor}
     renderItem={renderRow} 
 />

,然后将renderRow()函数包装在useCallback挂钩中。

问题在于我的数据源data = {upcomingMovies}是具有2个级别的对象的数组,例如,我需要使用itemData.item.title来检索标题值。

虽然它可以在我当前的renderItem设置中工作,但是当我在

的外部函数中使用它时将无法工作
  const renderRow = ({ itemData }) => {
    return (
      <HomeItem
        id={itemData.item.id}
        poster={itemData.item.poster_path}
        title={itemData.item.title}
        onAssetSelection={fetchMovieHandler}
      />
    );
  };

我只是得到“ item”变量不可用

关于如何修改功能的任何建议?

答案 1 :(得分:0)

如果我是你,我将更改以下内容:

  1. 不要将内联函数放在FlatList(keyExtractor,renderItem)中!将其放置到单独的功能中。
  2. 使用简单的Component扩展名而不是PureComponent,并将您的道具与`shouldComponentUpdate比较。 (我认为传递的函数onAssetSelection导致重新渲染。检查它。)
  3. 您不需要使用React.memo,因为它用于功能组件。