
在 react 中修改嵌套对象状态时,需通过深拷贝防止原始数据被意外修改;本文介绍使用 `useref` + `json.parse(json.stringify())` 实现初始化快照,并在输入清空时精准还原原始值。
在 React 应用中,当状态包含嵌套对象(如 stateObj.option.c)并需要在用户操作后回退到初始值时,浅拷贝(如展开运算符 {...obj})无法满足需求——它仅复制第一层引用,深层属性仍共享内存地址,导致状态污染。此时必须采用深拷贝(deep clone) 策略。
✅ 推荐方案:useRef + JSON.parse(JSON.stringify()) 快照初始化
该方法在组件挂载时一次性捕获初始嵌套对象的完整副本,并持久保存于 ref 中(不触发重渲染),后续可安全用于还原:
import React, { useState, useEffect, useRef } from 'react';
function FormComponent({ onChange }) {
const [stateObj, setStateObj] = useState({
a: "one",
b: 2,
option: {
c: "value"
}
});
// 使用 useRef 存储初始 option 的深拷贝快照
const initialOption = useRef(null);
useEffect(() => {
// ⚠️ 注意:仅在首次挂载时执行,避免重复赋值
initialOption.current = JSON.parse(JSON.stringify(stateObj.option));
}, []); // 空依赖数组确保只运行一次
const handleInputChange = (path, value) => {
if (value === "") {
// 输入为空时,还原整个 option 对象为初始快照
setStateObj(prev => ({
...prev,
option: initialOption.current
}));
} else {
// 否则按路径更新(此处简化为直接覆盖 c 字段)
setStateObj(prev => ({
...prev,
option: {
...prev.option,
c: value
}
}));
}
// 通知父组件(如需)
if (onChange) onChange(path, value);
};
return (
handleInputChange(["option", "c"], e.target.value)}
onBlur={(e) => {
if (e.target.value === "") {
handleInputChange(["option", "c"], "");
}
}}
placeholder="编辑选项 c"
/>
);
}
export default FormComponent;? 关键说明与注意事项
JSON.parse(JSON.stringify(obj)) 的适用边界:
适用于纯 JSON 数据(字符串、数字、布尔、null、数组、普通对象)。不支持 Date、RegExp、Map、Set、undefined、函数或循环引用。若状态含此类值,应改用 structuredClone()(现代浏览器支持)或成熟库如 lodash.cloneDeep()。useRef vs useState:
ref 用于存储“非渲染相关”的可变值,其变更不会触发组件重渲染,正适合存放初始化快照这类静态基准数据。避免 useEffect 依赖项陷阱:
初始化快照必须放在 useEffect 中且依赖数组为空 [],否则 stateObj.option 变化会反复重置快照,失去“初始值”意义。-
更健壮的路径更新(进阶):
若需支持任意嵌套路径(如 ["option", "nested", "c"]),可封装通用工具函数:const setByPath = (obj, path, value) => { const result = { ...obj }; const keys = [...path]; const lastKey = keys.pop(); let cursor = result; for (const key of keys) { cursor = cursor[key] = { ...cursor[key] }; } cursor[lastKey] = value; return result; };
✅ 总结
深拷贝状态对象是 React 处理嵌套数据变更的基石能力。对于简单 JSON 结构,JSON.parse(JSON.stringify()) 配合 useRef 是轻量、可靠、零依赖的解决方案;对复杂场景,优先考虑 structuredClone() 或专业工具库。始终牢记:永远不要直接修改 state 引用,所有更新必须基于不可变原则生成新对象。










