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

如何避免列表整体重渲染?React应用优化重构咨询

优化React列表重渲染问题的方案

先拆解原代码里导致不必要重渲染的核心问题,再一步步重构优化:

原代码的核心问题

  1. 状态更新直接修改原对象:handleUpdate里直接修改prevList[0].value,属于违反React状态不可变原则的写法,虽然后续返回了新数组,但这种操作可能引发逻辑隐患。
  2. 列表key使用不稳定值:用随机生成的value作为key,每次更新第一行时key都会变化,触发组件卸载+重新挂载,完全违背React的diff优化逻辑。
  3. Memo比较逻辑无效:自定义的memo比较函数依赖children.props.children,但React元素是引用类型,父组件每次渲染都会生成新的元素实例,这个比较逻辑根本不生效,导致所有列表项都会重渲染。
  4. 组件接收冗余props:Row组件定义了label和valueprops,但实际未使用,父组件也未传递,属于无效代码。

重构优化步骤

1. 修复状态更新的不可变性

修改handleUpdate,创建新的对象而非直接修改原数组元素:

const handleUpdate = useCallback(() => {
  setList((prevList) => {
    // 创建新的第一行对象,不修改原数组元素
    const newFirstItem = { ...prevList[0], value: Math.random() };
    // 替换原数组第一个元素,返回全新数组
    return [newFirstItem, ...prevList.slice(1)];
  });
}, []);

2. 设置稳定的列表key

用label(或固定的index,因列表长度静态不变)作为key,保证key的稳定性:

{
  list.map((item) => (
    <MRow key={item.label}>
      <b>{item.value}</b>
    </MRow>
  ))
}

3. 正确使用memo优化组件

简化Row组件的props,让memo的默认浅比较生效,同时给Button也加上memo(可选,避免父组件渲染时Button的无意义重渲染):

// 优化Button组件
const Button = memo(function Button(props) {
  const { children, onClick } = props;
  return <button onClick={onClick}>{children}</button>;
});

// 简化Row组件,只接收必要的children
function Row({ children }) {
  return <div style={{ marginTop: '8px' }}>{children}</div>;
}

// 使用默认的memo浅比较即可,无需自定义逻辑
const MRow = memo(Row);

4. 完整优化后的代码

import ReactDOM from 'react-dom/client';
import React, { useState, useCallback, memo } from "react";

const initList = () => {
  return Array.from({ length: 10 }, (_el, index) => ({
    value: Math.random(),
    label: `row ${index + 1}`
  }));
};

function App() {
  const [list, setList] = useState(initList());

  const handleUpdate = useCallback(() => {
    setList((prevList) => {
      const newFirstItem = { ...prevList[0], value: Math.random() };
      return [newFirstItem, ...prevList.slice(1)];
    });
  }, []);

  return (
    <div>
      <h1>List App</h1>
      <Button onClick={handleUpdate}>Update "row 1"</Button>
      {list.map((item) => (
        <MRow key={item.label}>
          <b>{item.value}</b>
        </MRow>
      ))}
    </div>
  );
}

const Button = memo(function Button(props) {
  const { children, onClick } = props;
  return <button onClick={onClick}>{children}</button>;
});

function Row({ children }) {
  return <div style={{ marginTop: '8px' }}>{children}</div>;
}

const MRow = memo(Row);

ReactDOM.createRoot(document.querySelector('#root')).render(<App />);

验证效果

点击按钮后,仅App、Button和第一行的MRow会触发渲染,其余9行不会重渲染,完全消除了不必要的性能消耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:17:36