You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 08:12:58