
在 React 中直接对 state 中的数组调用 push() 或修改对象属性会触发 “Cannot add property 0, object is not extensible” 错误,根本原因是 React(尤其配合 Redux Toolkit 或严格模式)冻结了 state 对象以保障不可变性。
在 react 中直接对 state 中的数组调用 `push()` 或修改对象属性会触发 “cannot add property 0, object is not extensible” 错误,根本原因是 react(尤其配合 redux toolkit 或严格模式)冻结了 state 对象以保障不可变性。
该错误典型出现在使用 useState 管理含嵌套数组(如 permissions: [])的状态,并在事件处理器中尝试原地修改数组或其内部对象时。例如:updatedFormData.permissions.push(...) 或 permission.name = ... —— 即使你对顶层对象做了展开({...formData}),permissions 数组及其内部对象仍为引用复用,而 React/Redux 默认将 state 深度冻结(Object.freeze),导致任何赋值操作失败。
✅ 正确做法是:始终返回全新对象/数组,避免任何突变(mutation),并优先使用函数式更新(setFormData(prev => {...}))确保基于最新状态计算。
✅ 推荐修复方案(不可变更新 + 函数式 setState)
const handleCheckboxChange = (
permissionId: number,
section: string,
checkedValues: CheckboxValueType[]
) => {
setFormData((prev) => {
const name = checkedValues.length > 0 ? String(checkedValues[0]) : "";
// 查找是否已存在该 permission 条目
const existingIndex = prev.permissions.findIndex(
(per) => per.permissionId === permissionId && per.section === section
);
if (existingIndex !== -1) {
// 更新已有项:map 创建新数组,对匹配项返回新对象
return {
...prev,
permissions: prev.permissions.map((per, idx) =>
idx === existingIndex ? { ...per, name } : per
),
};
} else {
// 新增条目:使用 concat(更语义化)或 [...prev.permissions, newItem]
return {
...prev,
permissions: [
...prev.permissions,
{ permissionId, section, name }
],
};
}
});
};
⚠️ 关键注意事项
- 不要依赖闭包中的 formData:初始写法 const updatedFormData = { ...formData } 在异步或多次快速点击时可能读取过期状态,函数式更新(setFormData(prev => ...))可彻底规避此问题。
- 深拷贝 ≠ 浅拷贝:{...formData} 只复制顶层属性,formData.permissions 仍是原引用;若需更新数组内对象,必须对数组及目标元素分别展开({...per})。
- 避免 Array.push()、Array.pop()、obj.prop = value 等突变方法:一律替换为 concat、filter、map、扩展运算符等纯函数式操作。
- 检查 Redux 配置:若使用 Redux Toolkit(RTK),其 createSlice 默认启用 immer,但若手动返回新对象(如 state.addRole = {...}),仍需确保所有嵌套结构不可变;本例中 addRole.permissions 若来自 Redux,也应遵循同样原则初始化与更新。
? 验证与调试建议
- 在 useEffect 中打印 Object.isFrozen(formData.permissions),确认冻结状态;
- 使用 React DevTools 的 “Highlight Updates” 功能观察重渲染是否合理;
- 对复杂嵌套结构,可考虑使用 immer 简化逻辑(但需理解其代理机制)。
遵循不可变更新原则,不仅能解决当前报错,更是构建可预测、易调试、高性能 React 应用的基石。











