Redux Toolkit存储数组复制后修改属性报错:为何需使用JSON.stringify和JSON.parse实现深拷贝?
为什么必须用JSON.stringify/JSON.parse才能修改Redux Toolkit中的数组元素?
这事儿核心原因在于两种复制方式的深浅差异,再加上Redux Toolkit(RTK)对状态的特殊处理,咱们一步步拆解:
1. 扩展运算符[...options]是浅拷贝,碰了RTK的只读代理
当你用[...options]复制数组时,你只是复制了数组本身的"外壳",但数组里的每个Options对象还是和RTK store里的原对象共用同一个引用。
而RTK为了强制保证状态的不可变性,会用Immer把store里的状态包装成只读的代理对象——这些对象的属性被冻结了,你直接修改它们的属性(比如copy[0].last = true),本质上还是在触碰原store里的只读对象,自然就会抛出"无法赋值给只读属性"的错误。
2. JSON.stringify/JSON.parse是深拷贝,彻底脱离原代理
JSON.parse(JSON.stringify(options))做了一次完整的深拷贝:它把整个数组和里面的所有对象都转换成无关联的JSON字符串,再重新解析成全新的JavaScript对象。这些新对象完全脱离了RTK store的代理包装,不再受只读限制,所以你可以自由修改它们的属性。
额外推荐:更符合RTK规范的写法
其实用JSON方法虽然能解决问题,但并不是最优解——它没法处理函数、Symbol这类JSON不支持的类型。RTK内置了Immer,你可以用更安全的方式处理状态:
- 如果是在reducer里修改store状态:
const optionsSlice = createSlice({ name: 'options', initialState: [] as Options[], reducers: { markFirstOptionAsLast: (state) => { // Immer允许你直接"修改",它会自动生成不可变的新状态 if (state[0]) { state[0].last = true; } } } });
- 如果是在组件里生成临时修改的副本:
import { produce } from '@reduxjs/toolkit'; const modifiedOptions = produce(options, (draft) => { draft[0].last = true; });
这种写法既安全,又符合RTK的设计逻辑。
内容的提问来源于stack exchange,提问作者Codehan25
相关产品推荐
相关产品推荐

