更新对象 state
为什么修改对象字段不能代替更新状态?
- 旧对象: 已存入 state 的对象属于先前快照,直接赋值会同时篡改所有共享该引用的地方,且不会主动请求渲染;
- 新对象: 创建下一对象并交给 setter,既让 React 得到新引用,也保留旧快照以便调试和回退;
- 局部创建: 刚创建、尚未共享的新对象可以先修改,再交给 setter;
setPosition({ x: event.clientX, y: event.clientY });
如何只修改对象的一部分字段?
- 浅拷贝:
{ ...person, name: nextName }复制一层属性并覆盖 name,展开本身不会递归复制嵌套对象; - 覆盖顺序: 后面的同名字段覆盖前面,通常先展开旧值再写变更;
function handleChange(event) {
const { name, value } = event.target;
setPerson((previous) => ({ ...previous, [name]: value }));
}
如何更新嵌套对象而不修改旧引用?
- 复制路径: 从修改点向上复制每一级对象,使从新根对象到修改点的路径都指向新引用;
- 引用模型: 看起来“嵌套”的对象实际由引用连接,同一 artwork 可能被多个对象共享,不能直接改它;
setPerson((previous) => ({
...previous,
artwork: {
...previous.artwork,
city: "上海",
},
}));
深层更新过于繁琐时怎样调整?
- 扁平结构: 优先评估按 ID 索引的状态结构,减少路径层数;
- Immer: 可以对 draft 使用修改式写法,由库生成不可变结果;draft 不是允许直接修改原 state,库用法见生态中的 Immer 笔记;
- 取舍: 简单对象无需额外依赖,复杂结构才需要比较复制成本与工具引入成本;