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

React优化:如何仅重渲染数组中变更的单个子组件

React列表渲染优化:仅重渲染被点击的子组件

问题根源

  1. 子组件的handleChange每次都是新函数:你在list.map中给Child传递的handleChange是内联箭头函数(checked) => handleChange(checked, item.id),这个函数会在父组件每次渲染时重新创建,导致每个Child的handleChange props都发生变化,即便用了useCallback缓存顶层函数也无法避免。
  2. 父组件状态更新触发全量渲染:调用setList返回新数组时,React通过浅比较发现数组引用变化,会触发App组件重渲染;默认情况下子组件会跟随父组件一起重渲染,除非做了缓存处理。
  3. 状态更新的可变操作:你直接修改了oldList中的对象属性(oldList[i].checked = checked),虽然最后返回了新数组,但这种可变操作不符合React不可变状态原则,可能引发意外渲染问题。

React数组状态对比机制

React使用浅比较判断状态是否需要更新:

  • 对于数组,仅检查新旧数组的引用是否相同,不会遍历元素做深层对比。
  • 若状态更新返回新数组(比如用[...oldList]或map生成),引用不同则判定状态变化,触发组件重渲染。
  • 数组中的对象元素,浅比较只对比对象引用,不检查内部属性。即使对象属性变化但引用不变,浅比较会认为元素无变化。

解决方案

步骤1:用React.memo缓存子组件

给Child组件包裹React.memo,让它仅在props浅比较变化时重渲染:

// Child.js
import { memo } from "react";

const Child = ({ text, checked, handleChange, id }) => {
  console.log(`Child ${text} 重渲染`);
  return (
    <div
      style={{
        display: "flex",
        border: "1px solid green",
        justifyContent: "space-around"
      }}
    >
      <p>{text}</p>
      <input
        style={{ width: "20rem" }}
        type="checkbox"
        checked={checked}
        onChange={(e) => handleChange(e.target.checked, id)}
      />
    </div>
  );
};

export default memo(Child);

步骤2:传递稳定的handleChange,避免内联函数

修改App组件,不再在map中创建内联箭头函数,直接传递useCallback缓存的handleChange,并将id作为props传给Child:

// App.js
import { useCallback, useState } from "react";
import Child from "./Child";
import "./styles.css";

const mockList = [
  { text: "1", id: 1, checked: false },
  { text: "2", id: 2, checked: false },
  { text: "3", id: 3, checked: false },
  { text: "4", id: 4, checked: false },
  { text: "5", id: 5, checked: false }
];

export default function App() {
  const [list, setList] = useState(mockList);

  const handleChange = useCallback((checked, id) => {
    // 不可变状态更新:map生成新数组,修改项创建新对象
    setList((oldList) =>
      oldList.map((item) =>
        item.id === id ? { ...item, checked } : item
      )
    );
  }, []);

  return (
    <div className="App">
      {list.map((item) => (
        <Child
          key={item.id}
          text={item.text}
          checked={item.checked}
          handleChange={handleChange}
          id={item.id}
        />
      ))}
    </div>
  );
}

关键说明

  • React.memo:对Child的props做浅比较,只有text、checked、handleChange、id中任意一个值(或引用)变化时,才会重渲染对应子组件。
  • 稳定的handleChange:通过useCallback缓存的函数引用始终不变,避免了因函数引用变化导致的子组件重渲染。
  • 不可变状态更新:用map和扩展运算符创建新对象,确保状态更新的不可变性,符合React最佳实践,也避免了潜在的渲染异常。

修改后,点击某个复选框时,只有对应的Child组件会重渲染,其他子组件不会产生不必要的渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 12:24:19