React Hook修改数组State时报只读属性赋值错误该如何解决
问题原因分析
- 你对
listLockerUserType做的是浅拷贝:[...listLockerUserType]只复制了数组本身,数组内部存储的对象依然和原state里的对象共享引用 - 如果原state的对象是不可变类型(比如来自Redux的不可变数据、用了Immer产生的冻结对象、或者你之前对state做过
Object.freeze处理),直接修改引用指向的对象属性就会触发只读报错 - 你当前直接操作
listLocker[index][属性]的写法不符合React状态更新的不可变原则,即使没报错也可能导致组件不触发重渲染等异常问题
修复方案
遵循React不可变更新原则,修改后的代码如下:
const [listLockerUserType, setListLockerUserTypes] = useState([]) const getData = (content) => { const contentLocker = {...content} const keys = Object.keys(contentLocker) const targetKey = keys[1] // 使用函数式更新拿到最新的state,避免闭包问题 setListLockerUserTypes(prev => { return prev.map(item => { // 仅修改匹配的项,其余项原样返回 if(item.userTypeId === content.id) { // 浅拷贝要修改的对象,再更新属性 return { ...item, [targetKey]: content[targetKey] } } return item }) }) console.log(content) }
该方案优势:
- 完全遵循不可变更新逻辑,所有修改都基于新对象/新数组,不会触发只读属性报错
- 性能开销低,仅拷贝需要修改的单个对象,无需全量深拷贝整个数组
- 避免原写法中
index=-1(找不到匹配项)时的数组越界报错
额外排查思路
如果修改后仍存在相同报错,可做如下验证:
- 打印判断原state的对象是否被冻结:
console.log(Object.isFrozen(listLockerUserType[0])),如果返回true说明上游数据源本身是冻结状态,所有修改操作必须先拷贝对象再操作 - 检查项目是否集成了Immer、Redux等强制不可变状态的库,这类场景下禁止直接修改原状态的任何属性
内容的提问来源于stack exchange,提问作者Minh Huy
相关产品推荐
相关产品推荐

