React Native:为什么FlatList会在数据更改时完全重新呈现?

问题描述

感谢您阅读我的问题!自几天以来,我一直在努力解决这个问题:每当我对基础数据进行更改时,我的Flatlist组件都会重新渲染列表中的所有项目。

情况:

  • 我有一个FlatList组件,用于渲染Items,其中包含一个TouchableOpacity对象,用于在该项目上切换收藏夹状态。
  • 如果按下此按钮,我希望仅此特定项目能够在我的FlatList中更改/重新呈现,而不是所有项目。感觉就像我通过调用setListData更新状态一样,它重新呈现了所有内容
  • 我在更复杂的设置中遇到了此问题,但能够深入研究此核心问题。还是这实际上是预期的行为?

代码

import React,{ useState } from "react";
import {
  View,Text,StyleSheet,FlatList,TouchableOpacity,} from "react-native";

const PlanerScreen = () => {
  const [listData,setListData] = useState([
    { id: "1",name: "Banana",isFav: true },{ id: "2",name: "Apple",isFav: false },]);

  const Item = ({ item,onPressHandler }) => {
    console.log(item.name," rendered");
    const color = item.isFav ? "red" : "green";
    return (
      <View
        style={{
          flexDirection: "row",width: "100%",margin: 10,}}
      >
        <Text>{item.name}</Text>
        <TouchableOpacity
          style={{ width: 100,height: 50,backgroundColor: color }}
          onPress={onPressHandler}
        />
      </View>
    );
  };

  const favHandler = (id) => {
    setListData(
      listData.map((item) =>
        item.id === id ? { ...item,isFav: !item.isFav } : item
      )
    );
  };

  console.log("FlatList rendered");
  return (
    <View style={{ flex: 1 }}>
      <StatusBar style={selectedTheme === "light" ? "dark" : "light"} />
      <FlatList
        data={listData}
        renderItem={({ item }) => (
          <Item item={item} onPressHandler={() => favHandler(item.id)} />
        )}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
};

export default PlanerScreen;

单击“收藏夹切换”按钮时控制台输出

FlatList rendered
Banana  rendered
Apple  rendered
FlatList rendered
Banana  rendered
Apple  rendered
FlatList rendered
Banana  rendered
Apple  rendered

解决方法

对于功能组件,可以使用React.memo来替代shouldComponentUpdate。 它会告诉React基于上一个和下一个道具何时重新渲染组件。

import React,{ useState } from "react";
import {
  View,Text,StyleSheet,FlatList,TouchableOpacity,} from "react-native";

const styles = StyleSheet.create({
  container: {
    flex: 1,}
})

const keyExtractor = (item) => item.id;

const Item = React.memo(({ item,onPressHandler }) => {
  console.log(item.name," rendered");
  const color = item.isFav ? "red" : "green";
  return (
    <View
      style={{
        flexDirection: "row",width: "100%",margin: 10,}}
    >
      <Text>{item.name}</Text>
      <TouchableOpacity
        style={{ width: 100,height: 50,backgroundColor: color }}
        onPress={() => onPressHandler(item.id)}
      />
    </View>
  );
},(prevProps,nextProps) => {
  if (prevProps.item.isFav === nextProps.item.isFav) return true;
  return false;
});

const PlanerScreen = () => {
  const [listData,setListData] = useState([
    { id: "1",name: "Banana",isFav: true },{ id: "2",name: "Apple",isFav: false },]);

  const favHandler = (id) => {
    setListData(prevState => {
      return prevState.map((item) =>
          item.id === id ? { ...item,isFav: !item.isFav } : item
        )
      }
    );
  }


  console.log("### FlatList rendered #####");
  return (
    <View style={styles.container}>
      <FlatList
        data={listData}
        renderItem={({ item }) => <Item item={item} onPressHandler={favHandler} />}
        keyExtractor={keyExtractor}
      />
    </View>
  );
};

export default PlanerScreen;