网站搜索

javascript - 无法分配给对象 '#<Object>' 的只读属性 'property'


我有这个问题。

带接口:

export interface IDevice {
  id: string,
  selected: boolean
}

创建实例方式:

let newDevice: IDevice = {
  id: uuid4(),
  selected: false,
} as IDevice;

它被添加到 Recoil 状态中的数组中,并在 React 中用于一个函数,该函数中数组是通过 useRecoilState() 获取的。const [leftList, setLeftList]=React.useState<IDevice[]>([]);

现在它被用在一个用于在列表控件上选择设备的处理程序中,这里发生了错误:

...
leftList.map((item: IDevice) => {     
      if (item.id === event.dataItem.id) {
        item.selected = !item.selected;
      }
      return item;
    })
...

我收到错误:无法分配给对象 '#' 的只读属性 'selected'

即使先通过 [...leftList] 克隆数组也没有帮助。

我迷路了:-) 希望有人能对此提供一些见解?

答案#1

状态不应该被直接修改,这正是你现在在 .map() 方法中通过更新对象所做的。TS 正在通过将你的对象设为只读来告诉你不要这样做。

相反,你可以创建一个包含 item 所有属性的新对象(使用展开语法 ... 实现),以及一个新的覆盖属性 selected,如果 id 与事件的数据项 id 匹配,它将使用该项当前 selected 值的取反版本,否则将保持原来的 selected 值:

leftList.map((item: IDevice) => ({
  ...item, 
  selected: item.id === event.dataItem.id ? !item.selected : item.selected
}))

使用扩展语法 ([...leftList]) 克隆数组只会进行浅拷贝,而不会对其中的对象进行深拷贝,因此,在 .map() 中修改对象引用仍然会修改原始状态。

或者,你可以不用每次都展开 item 对象并创建一个新对象(正如 @3limin4t0r 指出的,这可能会稍微影响性能),而是仅在你想修改 selected 属性时才返回一个新创建的对象:

leftList.map((item: IDevice) => {     
  if (item.id !== event.dataItem.id) return item;
  return {...item, selected: !item.selected};
});