React优化:如何仅重渲染数组中变更的单个子组件
React列表渲染优化:仅重渲染被点击的子组件
问题根源
- 子组件的
handleChange每次都是新函数:你在list.map中给Child传递的handleChange是内联箭头函数(checked) => handleChange(checked, item.id),这个函数会在父组件每次渲染时重新创建,导致每个Child的handleChangeprops都发生变化,即便用了useCallback缓存顶层函数也无法避免。 - 父组件状态更新触发全量渲染:调用
setList返回新数组时,React通过浅比较发现数组引用变化,会触发App组件重渲染;默认情况下子组件会跟随父组件一起重渲染,除非做了缓存处理。 - 状态更新的可变操作:你直接修改了
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
相关产品推荐
相关产品推荐

