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

未改变属性时重建React状态对象是否为不良实践?

替换React状态中未变化的对象是否属于不良实践?会有显著性能开销吗?

我理解React要求props视为不可变的原因,但我有个疑问:替换那些未发生变化的对象,算不算不良实践?会不会带来明显的性能开销?

举个例子,假设我有这样的状态:

let gameObjects = [
 { id: 'a', pos: {x: 0, y: 0}, name: 'alice' },
 { id: 'b', pos: {x: 0, y: 0}, name: 'bob' },
 { id: 'c', pos: {x: 0, y: 0}, name: 'charles' },
 // ...数百个对象
]
// 将gameObjects传入函数式React组件进行渲染

现在假设我只修改了两个对象的位置,但重建了所有对象:

gameObjects[0].pos = {x: 1, y: 1}
gameObjects[1].pos = {x: 1, y: 1}

gameObjects = gameObjects.map((obj) => {
  return {
    pos: {...obj.pos},
    ...obj,
  }
})
// 将gameObjects传入函数式React组件进行渲染

我是否应该特意只重建那些发生变化的对象?我知道这样做能避免React对每个对象做深度检查,但我觉得就算重建所有对象,React也足够“智能”,只会在嵌套属性变化时更新DOM。

我的场景中状态变化来自网络,上面的代码只是示例,我更关注通用情况。


核心结论

替换未变化的对象属于没必要的性能损耗操作,虽然不一定立刻引发严重问题,但在数据量较大(比如你例子里的数百个对象)的场景下,会累积出可感知的性能开销,属于应该避免的实践。

具体原因分析

  1. React的浅比较机制
    React判断组件是否需要更新时,默认用浅比较:对比props/state的引用是否变化,而非深度对比内容。如果你重建所有对象,哪怕内容完全没变,每个对象的引用都发生了改变,React会认为这些对象都“更新”了,进而触发对应子组件的重新渲染——哪怕这些子组件的UI完全不需要变化。

  2. 额外的内存与计算开销
    每次通过map重建所有对象时,都会创建数百个新的对象实例,这会增加JavaScript引擎的垃圾回收压力。对于频繁更新的场景(比如游戏实时状态、高频网络同步),这种无意义的对象创建会累积成明显的性能负担。

  3. React的“智能”是有前提的
    你提到的“React只会在嵌套属性变化时更新DOM”,其实是指React的DOM diffing机制——但这一步是在组件触发重新渲染之后才会执行的。也就是说,哪怕最终DOM没变化,子组件的render函数还是会先执行一遍,这本身就会消耗计算资源(比如虚拟DOM的创建与对比)。

正确的做法

只重建确实发生变化的对象,保留未变化对象的引用:

// 假设我们已知需要更新的id列表是['a', 'b'],或通过对比新旧pos判断
gameObjects = gameObjects.map(obj => {
  // 只有pos变化的对象才重建
  if (obj.id === 'a' || obj.id === 'b') {
    return {
      ...obj,
      pos: {...obj.pos} // 这里因为pos已经是新对象,其实可直接用obj.pos,但保持不可变习惯更好
    }
  }
  // 未变化的对象直接返回原引用
  return obj
})

如果状态更新来自网络,可以在接收数据时先对比新旧对象的差异,只更新有变化的条目,避免全量重建。

补充优化建议

  • 如果子组件是纯组件(比如用React.memo包裹的函数组件),浅比较会直接跳过未变化对象对应的子组件渲染,这时候保留原引用的收益会更明显。
  • 对于嵌套较深的状态,可以考虑使用Immer这类库简化不可变更新的代码,它会自动帮你只修改需要变化的部分,保留其他对象的引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 10:12:40