SectionList单个复选框切换触发全列表重渲染的优化需求
问题:SectionList切换单个复选框引发全列表重渲染卡顿
我实现了一个SectionList,每个列表项为独立组件,包含复选框及其他视图,复选框默认隐藏,长按任意项可显示复选框。
问题详情
切换单个项的复选框时,SectionList内所有项都会重渲染,导致应用出现轻微卡顿,希望仅重渲染被更新的项。
页面代码
export const MyScreen: React.FC<Props> = ({}) => { const [itemSelectionMode, setItemSelectionMode] = useState<boolean>(false); const [selectedItemIDs, setSelectedItemIDs] = useState<number[]>([]); // 使用RTK-Query获取数据 const { data: events, error, isFetching, } = useGetItemsQuery({ date: someDate, }); const onItemCheckBoxValueChange = ( selected: boolean, itemID: number, ): void => { if (selected) { setSelectedItemIDs([...selectedItemIDs, itemID]); } else { setSelectedItemIDs(selectedItemIDs.filter(id => id !== itemID)); } }; const onItemCardLongPress = (): void => { setItemSelectionMode(true); }; const renderListItem = (item: Item): JSX.Element => { return ( <ListItem item={item} onLongPress={onItemCardLongPress} selectionMode={eventSelectionMode} // 注意:此处存在笔误,应为itemSelectionMode onCheckboxValueChange={onItemCheckBoxValueChange} selected={selectedItemIDs.includes(item.id)} /> ); }; const renderItemList = (): JSX.Element => { return ( <SectionList sections={items} renderItem={({ item }) => renderListItem(item)} renderSectionHeader={({ section: { title } }) => <Text>{title}</Text>} /> ); }; return ( <View style={style.mainContainer}> {renderEventList()} {/* 注意:此处存在笔误,应为renderItemList() */} </View> ); }
ListItem.tsx代码
const renderItemDetails = (details: string) => { return <Text style={style.details}>{details}</Text>; }; const renderItemAddress = (address: string) => { if (!address) return null; return <Text style={style.address}>{address}</Text>; }; export const EventListItem: React.FC<EventListItemProps> = ({ item, onLongPress, selectionMode = false, onCheckboxValueChange, selected = false, }) => { const address = someUtilFunction(item.address); const details = someUtilFunction(item.boxes); return ( <Card style={style.card} key={item.id} onLongPress={onLongPress}> {/* 其他视图 */} <View style={style.headerContainer}> {selectionMode && ( <Checkbox value={selected} onValueChange={(value) => { onCheckboxValueChange(value, item.id); }} /> )} </View> {renderItemAddress(address)} {renderItemDetails(details)} {/* 其他视图 */} </Card> ); };
解决方案
1. 用React.memo优化ListItem组件
ListItem属于纯组件,仅当props变化时才需要重渲染。用React.memo包裹组件,避免不必要的重渲染:
// 修改ListItem的导出 export const EventListItem = React.memo<EventListItemProps>(({ item, onLongPress, selectionMode = false, onCheckboxValueChange, selected = false, }) => { // 原有组件逻辑 }, (prevProps, nextProps) => { // 可选:自定义props比较逻辑,仅当关键属性变化时才重渲染 return ( prevProps.item.id === nextProps.item.id && prevProps.selectionMode === nextProps.selectionMode && prevProps.selected === nextProps.selected && prevProps.onLongPress === nextProps.onLongPress && prevProps.onCheckboxValueChange === nextProps.onCheckboxValueChange ); });
2. 用useCallback保持回调函数引用稳定
每次渲染时创建新的回调函数会导致ListItem的props变化,触发重渲染。用useCallback包裹回调,保持引用稳定:
const onItemCheckBoxValueChange = useCallback((selected: boolean, itemID: number): void => { // 使用函数式更新,避免闭包问题 setSelectedItemIDs(prev => { if (selected) { // 避免重复添加 return prev.includes(itemID) ? prev : [...prev, itemID]; } else { return prev.filter(id => id !== itemID); } }); }, []); const onItemCardLongPress = useCallback(() => { setItemSelectionMode(true); }, []);
3. 优化选中状态判断(改用Set替代数组)
数组的includes方法是O(n)时间复杂度,列表项较多时会影响性能。改用Set存储选中ID,判断选中状态的时间复杂度降为O(1):
// 初始化状态为Set const [selectedItemIDs, setSelectedItemIDs] = useState<Set<number>>(new Set()); // 更新逻辑 const onItemCheckBoxValueChange = useCallback((selected: boolean, itemID: number): void => { setSelectedItemIDs(prev => { const newSet = new Set(prev); selected ? newSet.add(itemID) : newSet.delete(itemID); return newSet; }); }, []); // 在renderListItem中判断选中状态 selected={selectedItemIDs.has(item.id)}
4. 给SectionList和ListItem设置稳定的Key
确保React能准确识别需要更新的项:
// SectionList添加keyExtractor <SectionList sections={items} renderItem={({ item }) => renderListItem(item)} renderSectionHeader={({ section: { title } }) => <Text>{title}</Text>} keyExtractor={(item) => item.id.toString()} // 唯一标识每个item /> // ListItem的key放在组件外层,而非内部Card上 const renderListItem = (item: Item): JSX.Element => { return ( <ListItem key={item.id} // 此处设置key item={item} onLongPress={onItemCardLongPress} selectionMode={itemSelectionMode} // 修正之前的笔误 onCheckboxValueChange={onItemCheckBoxValueChange} selected={selectedItemIDs.has(item.id)} /> ); };
内容的提问来源于stack exchange,提问作者Wahidullah Shadab
相关产品推荐
相关产品推荐

