状态变化时如何防止 FlatList 重新渲染

Posted

技术标签:

【中文标题】状态变化时如何防止 FlatList 重新渲染【英文标题】:How to prevent FlatList re rendering when state changes 【发布时间】:2021-02-22 19:02:04 【问题描述】:

我有一个非常大的 FlatList,它呈现 800 个带有图像和文本的按钮。此列表位于 TabScreen 内,该 TabScreen 位于主屏幕内,另一个 tabScreen 具有文本输入。当我更改此 textInput 中的文本时,setState 会触发我的平面列表,该列表在后台更新,再次呈现其大量项目并减慢一切。我怎样才能避免这种情况?

主屏幕:

export default function HomeScreen(props: any) 
  const Tab = createBottomTabNavigator();

  return (
    <View style=styles.home__container>
      <HeaderButton navigation=props.navigation />
      <Tab.Navigator
        screenOptions=( route ) => (
          tabBarIcon: ( focused, color, size ) => 
            let iconName: string = "";
            if (route.name === "Search") 
              iconName = "ios-search";
             else if (route.name === "List") 
              iconName = focused ? "ios-list-box" : "ios-list";
            
            return <Ionicons name=iconName size=size color=color />;
          ,
        )
        tabBarOptions=
          activeTintColor: "#ffcb05",
          inactiveTintColor: "#ffffff",
          style:  backgroundColor: "#2a75bb" ,
        
      >
        <Tab.Screen name="Search" component=TabSearchScreen />
        <Tab.Screen  name="List" component=TabListScreen />
      </Tab.Navigator>
      <PokemonSavedContainer navigation=props.navigation />
    </View>
  );

平面列表选项卡(TabListScreen):

const TabListScreen = () => 
  const flatListRef = useRef<FlatList< name: string >>(null);
  const scrollList = (index: number) => 
    if (flatListRef.current)
      flatListRef.current.scrollToIndex( index: index );
  ;
  const keyExtractor = (item: any) => `key-$item.name`;
  const renderItem = (item: any) => 
    return (
      <MySwipeable pokeNumber=item.index + 1 pokeName=item.item.name />
    );
  ;
  return (
    <View style=styles.pokedex__list>
      <FlatList
        data=pokedex
        ref=flatListRef
        renderItem=renderItem
        keyExtractor=(item) => keyExtractor(item)
        style=styles.pokedex__list__pokemons
        onScrollToIndexFailed=() => alert("something went wrong")
        getItemLayout=(data, index) => (
          length: 100,
          offset: 100 * index,
          index,
        )
      />
      <View style= flexDirection: "column", marginTop: 20 >
        <SmallButton onPress=() => scrollList(0) title="Kanto" />
        <SmallButton onPress=() => scrollList(151) title="Jhoto" />
        <SmallButton onPress=() => scrollList(251) title="Hoenn" />
        <SmallButton onPress=() => scrollList(386) title="Sinnoh" />
        <SmallButton onPress=() => scrollList(494) title="Unova" />
        <SmallButton onPress=() => scrollList(649) title="Kalos" />
        <SmallButton onPress=() => scrollList(721) title="Alola" />
        <SmallButton onPress=() => scrollList(809) title="Galar" />
      </View>
    </View>
  );
;
export default TabListScreen;

textInput 屏幕(TabSearch 屏幕):

export default function TabSearchScreen() 
  const pokemonState = useSelector((state: RootStore) => state.pokemon);

  return (
    <View style=styles.home__container>
      <Searchbar />
      pokemonState.pokemon && <PokeContainer />
    </View>
  );

搜索组件

export default function Searchbar() 
  const dispatch = useDispatch();
  const handleSubmit = () => 
    dispatch(GetPokemon(pokemonName));
  ;
  const [pokemonName, setPokemonName] = useState("");

  return (
    <View style=styles.home__search>
      <TextInput
        style=styles.home__search__textInput
        onChangeText=(value) => setPokemonName(value)
        value=pokemonName
      />
      <PokeBallButton title="search" onPress=handleSubmit />
    </View>
  );

FlatList 项目(称为 Myswipeable,因为我想赋予它滑动功能)

export interface MySwipeProps 
  children?: React.ReactNode;
  props?: any;
  pokeName: string;
  pokeNumber: number;

const MySwipeable: React.FC<MySwipeProps> = (
  children,
  pokeName,
  pokeNumber,
  ...props
) => 
  console.log("pokemon");
  const renderLeftActions = () => 
    return <Animated.View></Animated.View>;
  ;

  return (
    <Swipeable renderLeftActions=renderLeftActions>
      <View style=styles.button__list__container>
        <Image
          source=
            uri: `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/$pokeNumber.png`,
          
          style=styles.pokemonTiny
        />
        <Text style=styles.button__list__text>pokeName</Text>
      </View>
    </Swipeable>
  );
;

export default MySwipeable;

【问题讨论】:

【参考方案1】:

尝试使用 React.memo() 来防止您的平面列表重新呈现。

这是一篇有用的文章,解释了 React.memo() 是什么以及如何使用它。

prevent-re-renders-react-functional-components-react-memo

【讨论】:

感谢您的帮助!我使用了备忘录,情况有所改善。我把 MySwipeable 变成了一个备忘录组件,现在我明白了问题不是状态变化,而是键盘打开!有了备忘录,每次我关闭 android 键盘(使用后退按钮和回车按钮)时,平面列表都会呈现其项目。如果我不使用备忘录,flatlist 会呈现键盘打开或关闭的所有项目。这让我很生气。我认为键盘与整个屏幕的渲染有关

以上是关于状态变化时如何防止 FlatList 重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

状态变化时如何防止 useQuery 运行?

React(性能):如何防止每次状态更改时出现不必要的渲染? [复制]

保存页面时重新呈现 FlatList 项目

React Hook 功能组件防止重新渲染

如何反应原生重新渲染 Flatlist?

React Native:为啥 FlatList 会在数据更改时完全重新渲染?