使用RTK Query获取数据时无法修改对象数组属性的问题
RTK Query获取的数据无法修改属性:TypeError: Cannot assign to read only property
问题原因
RTK Query 的 fetchBaseQuery 默认会用 Object.freeze() 冻结返回的响应数据,目的是强制保证状态的不可变性,防止开发者意外修改缓存中的原始数据,避免状态不一致的问题。这就是你修改对象属性时触发只读错误的核心原因——即使复制了数组,若拷贝方式没有完全脱离原冻结对象的引用,依然会遇到权限问题。
解决方案
方案1:使用可靠的深拷贝生成可修改副本
推荐用现代浏览器支持的 structuredClone() 做深拷贝(比 JSON.parse(JSON.stringify) 更可靠,支持更多数据类型):
const Test = () => { const { data: categories, isLoading, isSuccess, isError, error, } = useGetCategoriesQuery(); if (isSuccess) { // 生成完全独立的可修改副本 const categoriesCopy = structuredClone(categories); categoriesCopy[0].title = 'new title'; // 后续可正常操作categoriesCopy } return <h1>Something rendered</h1>; };
如果要兼容旧浏览器,JSON.parse(JSON.stringify) 依然可用,但需注意它无法处理函数、循环引用等特殊数据结构。
方案2:在API切片中提前处理响应(可选)
如果希望所有使用该查询的组件都直接拿到可修改的数据,可以在 transformResponse 中提前做深拷贝:
export const apiSlice = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: process.env.devUrl }), tagTypes: ['categories'], endpoints: (builder) => ({ getCategories: builder.query({ query: () => 'categories/includingJobs', // 提前深拷贝响应数据,返回可修改对象 transformResponse: (response) => JSON.parse(JSON.stringify(response.data.data)), providesTags: ['categories'], }), }), });
方案3:关闭冻结机制(不推荐)
可以在 fetchBaseQuery 配置中关闭数据冻结,但这会破坏RTK的不可变性保证,容易引发意外的缓存修改,仅在特殊场景下使用:
baseQuery: fetchBaseQuery({ baseUrl: process.env.devUrl, freeze: false // 关闭数据冻结 }),
注意事项
- 永远不要直接修改RTK Query返回的原始
data,必须生成副本后再操作 - 优先使用
structuredClone()作为深拷贝方式,兼容性和可靠性更好
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

