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

React点击时如何避免所有列表项重渲染?仅渲染变更项的问题排查与解决方案

解决React列表项不必要重新渲染的问题

我来帮你分析问题根源,一步步搞定这个性能优化问题:

问题核心

你尝试用React.memo和useCallback但没生效,主要是两个原因:

  1. onClick函数引用不稳定:在App组件里,每次渲染都会生成新的onClick函数,导致每个ListItem接收的onClick prop都是全新引用——哪怕用了React.memo,浅比较时也会判定props变化,触发全量重渲染。
  2. activeId全局更新触发所有项重渲染:当activeIndex改变时,所有ListItem的activeId prop都会同步更新,这也会让React.memo的浅比较失效,导致所有列表项重新渲染。

修复方案

我们从这两个点入手优化,具体步骤如下:

1. 用useCallback稳定onClick函数引用

在App.ts中导入useCallback,把点击函数包裹起来,确保它的引用在组件渲染时保持不变:

import React, { useState, useCallback } from "react";
import "./styles.css";
import ListItem from "./ListItem";

const items = [
  { id: 1, text: "items1" },
  { id: 2, text: "items2" },
  { id: 3, text: "items3" },
  { id: 4, text: "items4" },
  { id: 5, text: "items5" },
  { id: 6, text: "items6" },
  { id: 7, text: "items7" }
];

export default function App() {
  const [activeIndex, setActiveIndex] = useState(1);
  
  // 用useCallback缓存函数,依赖项为空(setActiveIndex是React提供的稳定引用)
  const onClick = useCallback((newActiveIndex: number) => {
    setActiveIndex(newActiveIndex);
  }, []);

  return (
    <div className="App">
      {items.map(({ id, text }) => {
        // 父组件提前计算当前项的激活状态
        const isActive = activeIndex === id;
        return (
          <ListItem 
            key={id} 
            id={id} 
            text={text} 
            isActive={isActive} 
            onClick={onClick} 
          />
        );
      })}
    </div>
  );
}

2. 修改ListItem组件,添加React.memo优化

把ListItem的props从activeId改为isActive,并用React.memo包裹组件,让它只在props发生实质性变化时重新渲染:

import React from "react";

interface ListItemProps {
  id: number;
  isActive: boolean; // 替换原有的activeId
  text: string;
  onClick: (newActiveIndex: number) => void;
}

const ListItem: React.FC<ListItemProps> = ({ id, isActive, text, onClick }) => {
  const style = {
    marginRight: "42px",
    marginTop: "24px",
    color: isActive ? "green" : "red"
  };
  console.log(`update id: ${id}`);
  return (
    <div>
      <span style={style}>id: {id}</span>
      <span style={style}>is active: {isActive ? "active" : "inactive"}</span>
      <span style={style}>text: {text}</span>
      <button onClick={() => onClick(id)}>setActive</button>
    </div>
  );
};

// 用React.memo包裹组件,实现浅比较优化
export default React.memo(ListItem);

优化效果说明

  • useCallback让onClick函数的引用保持稳定,不会因为App组件渲染而重新创建,避免了因这个prop变化导致的不必要重渲染。
  • 父组件提前计算isActive并传递给子组件,意味着每次只有之前激活的项(isActive从true变false)和新激活的项(isActive从false变true)会触发重渲染,其他项的props完全不变,React.memo会直接跳过它们的重渲染流程。

现在你点击按钮时,只会看到两个列表项的控制台输出,其他项不会被重新渲染啦!

内容的提问来源于stack exchange,提问作者harakiri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 14:47:38