React Native中如何修改FlatList已渲染项状态且不刷新其他项
问题:FlatList选中项变更时仅刷新对应项而非全部
当selectedIndex发生变化时,所有FlatList项都会刷新,因为selectedIndex是renderItem的依赖。我希望全局管理selectedIndex,仅让选中的单个项(或无项)显示详情,请问有没有办法只刷新selectedIndex指向的项?
原始代码
interface ItemType { title: string; detail: string; } const FlatListTestScreen = () => { const [selectedIndex, setSelectedIndex] = useState<number | null>(null); const data: ItemType[] = [ { title: 'title1', detail: 'detail1' }, { title: 'title2', detail: 'detail2' }, { title: 'title3', detail: 'detail3' }, { title: 'title4', detail: 'detail4' }, { title: 'title5', detail: 'detail5' }, ]; const renderItem = useCallback<ListRenderItem<ItemType>>( ({ item, index }) => { console.log('render', index, selectedIndex === index); return ( <Pressable onPress={() => { console.log('press', index); setSelectedIndex(index === selectedIndex ? null : index); }}> <Text>{item.title}</Text> {selectedIndex === index && <Text>{item.detail}</Text>} </Pressable> ); }, [selectedIndex], ); return <FlatList data={data} renderItem={renderItem} />; };
运行日志
LOG render 0 false LOG render 1 false LOG render 2 false LOG render 3 false LOG render 4 false LOG press 1 LOG render 0 false <- unnecessary render LOG render 1 true LOG render 2 false <- unnecessary render LOG render 3 false <- unnecessary render LOG render 4 false <- unnecessary render
解决方案
方法:抽离独立列表项组件并通过React.memo优化
核心思路是把单个列表项拆成独立组件,用React.memo做浅比较,只有当组件自身的props变化时才触发重渲染,避免因父组件状态变更导致所有项刷新。
import React, { useState, useCallback, memo } from 'react'; import { Pressable, Text, FlatList, ListRenderItem } from 'react-native'; interface ItemType { title: string; detail: string; } interface ListItemProps { item: ItemType; index: number; selectedIndex: number | null; onItemPress: (index: number) => void; } // 用React.memo包裹,仅当props发生变化时才重渲染 const ListItem = memo<ListItemProps>(({ item, index, selectedIndex, onItemPress }) => { console.log('render', index, selectedIndex === index); return ( <Pressable onPress={() => onItemPress(index)}> <Text>{item.title}</Text> {selectedIndex === index && <Text>{item.detail}</Text>} </Pressable> ); }); const FlatListTestScreen = () => { const [selectedIndex, setSelectedIndex] = useState<number | null>(null); const data: ItemType[] = [ { title: 'title1', detail: 'detail1' }, { title: 'title2', detail: 'detail2' }, { title: 'title3', detail: 'detail3' }, { title: 'title4', detail: 'detail4' }, { title: 'title5', detail: 'detail5' }, ]; // 用useCallback包裹点击事件,稳定引用 const handleItemPress = useCallback((index: number) => { setSelectedIndex(index === selectedIndex ? null : index); }, [selectedIndex]); const renderItem = useCallback<ListRenderItem<ItemType>>( ({ item, index }) => { return <ListItem item={item} index={index} selectedIndex={selectedIndex} onItemPress={handleItemPress} />; }, [selectedIndex, handleItemPress] ); return <FlatList data={data} renderItem={renderItem} />; };
优化后效果
点击index=1的项时,日志只会输出:
LOG render 1 true
(如果之前有选中项,比如之前选中了index=0,才会额外输出LOG render 0 false,其余项不会触发重渲染)
原理说明
- 原代码中
renderItem依赖selectedIndex,每次状态变更都会重新创建renderItem函数,导致FlatList所有项强制重渲染。 - 拆分独立组件后,
React.memo会对比每个ListItem的props:只有新选中的项和之前选中的项的selectedIndex === index结果会变化,这两个项的props对比会触发重渲染,其余项props无变化,不会刷新。
内容的提问来源于stack exchange,提问作者김지섭
相关产品推荐
相关产品推荐

