防止FlatList重新渲染导致性能问题。

问题描述 投票:0回答:1

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

主屏幕从API中获取一些数据,并以图片库的形式显示出来.主屏幕是父组件,它包括一个幻灯片库和一个Flatlist作为子组件。

1) Slideshow - 图片库自动播放,在每次图片迭代(每3秒)时运行一些操作(在用户点击它们的情况下获取URL) - 这将导致整个父组件及其子组件的重新渲染,但它每3秒渲染一次。

2)Flatlist - 只是一个简单的Flatlist,需要一个图像数组 - 这个 只应渲染一次 当父组件被首先加载时

问题

每隔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个日志条目(每个图像和渲染一个 - upcomingMovies有10个图像)。从用户界面上看,一切都很好,因为图像似乎没有改变,可能是因为我已经将HomeItem定义为PureComponent,并使用React.memo(HomeItem),但这造成了性能问题,因为我得到以下错误。

VirtualizedList: 你的列表很大,更新速度很慢--确保你的renderItem函数渲染的组件遵循React性能最佳实践,如PureComponent、shouldComponentUpdate等。

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

react-native-flatlist react-virtualized rerender conditional-rendering
1个回答
0
投票

如果我是你,我会修改以下内容。

  1. 不要把内联函数放在FlatList中(keyExtractor, renderItem)! 把它放在分离的函数中。
  2. 使用简单的 Component 延期而不是 PureComponent 并将您的道具与`shouldComponentUpdate.ComponentUpdate进行比较。我认为传递的函数onAssetSelection会导致重新渲染。检查一下)。)
  3. 你不需要使用 "shouldComponentUpdate"。React.memo 因为它是针对功能组件的。

0
投票

经过进一步的研究,并感谢@landorid的建议,我发现我需要通过在一个单独的函数中声明FlatList的renderItem,并使用回调钩和依赖我的数据源来控制它。

我试着将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}
      />
    );
  };

我只是得到,"项目 "变量不可用。

有什么建议,如何修改这个功能?

© www.soinside.com 2019 - 2024. All rights reserved.