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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 21:24:02