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

React Redux TypeError:不可扩展对象无法添加属性问题排查

问题:React-Redux中修改外部导出数组时出现"object is not extensible"错误

场景与代码

开发链接收藏网站,使用React-Redux管理状态,需求是将全量分类数据存于外部文件(后续对接后端),通过getCategories reducer根据集合ID筛选分类;添加新分类时同时更新Redux状态和外部Categories数组。

Category Slice代码

import { createSlice, current } from '@reduxjs/toolkit';
import { Categories, Collection } from '../TestData';
import {RandomId} from '../util/RandomId'

let categoryList = Categories

const categorySlice = createSlice({
  name: 'category',
  initialState: {
    value: categoryList,
    currentCollectionId: Collection[0].collectionId
  },
  reducers: {
    // Retrieve categories corresponding to collectionId
    getCategories: (state, { payload }) => {
      const collectionId = payload
      const latestCatList = []

      // Find the categories corresponding with the collectionId
      categoryList.map((item) => {
        if (item.collectionId === collectionId) {
          latestCatList.push(item)
        }
      })
      //Update the category list the categories
      state.value = [...latestCatList]
      //Update the current collectionId state
      state.currentCollectionId = collectionId
      return state
    },
    // Add new category
    addCategory: (state, { payload }) => {
      const newCategory = {
        collectionId: state.currentCollectionId,
        catId: RandomId(),
        name: payload.name,
        listEntries: payload.listEntries
      }
      state.value.push(newCategory) // Add new category object to state
      state.value = [...state.value]
            
      // NEED TO ADD CATEOGRY TO TEST DATA BUT FOR SOME GODDAMN REASON ITS NOT, maybe push to database straight instead
      categoryList.push(newCategory)
    },
  }   
})

外部Categories数组代码

export const Categories = [
  {
    collectionId: '1232', // Reference the collection the categories are under
    catId: '1232019283',
    name: 'FIT 2099', // Name of category
    listEntries: [ // Array of objects for each list entry
      {
        name: 'Assignment 1',
        link: 'Ed forums FIT 2099_ A1'
      },
      {
        name: 'Assignment 2',
        link: 'Ed forums FIT 2099_ A2'
      },
    ] 
  },
  {
    collectionId: '1232',
    catId: '1232120398',
    name: 'FIT 2004',
    listEntries: [
      {
        name: 'Tutorial 2',
        link: 'Master theore,'
      }
    ]
  },
  {
    collectionId: '1231',
    catId: '123109101',
    name: 'Movie List',
    listEntries: [
      {
        name: 'Harry potter 1',
        link: 'Hurawatch HP 1'
      }
    ] 
  },
]

错误信息

触发addCategory动作时,执行categoryList.push(newCategory)报错:

categorySlice.js:36 Uncaught TypeError: Cannot add property 3, object is not extensible
at Array.push ()
at addCategory (categorySlice.js:36:1)
at createReducer.ts:294:1
at produce (immerClass.ts:94:1)
at createReducer.ts:293:1
at Array.reduce ()
at reducer (createReducer.ts:260:1)
at reducer (createSlice.ts:372:1)
at combination (redux.js:560:1)
at E (:1:33326)

排查与解决方法

原因分析

ES模块中导出的const数组,在现代打包工具(如Webpack、Vite)的开发/生产模式下,会被自动冻结(Object.freeze),目的是防止意外修改模块级常量。你直接将Categories赋值给categoryList,本质上还是指向同一个被冻结的数组,因此push操作会触发"不可扩展"错误。

具体解决步骤

  1. 创建可修改的数组副本
    初始化时不要直接引用模块导出的数组,通过扩展运算符创建新的可扩展副本:

    // 替换原来的 let categoryList = Categories
    let categoryList = [...Categories];
    
  2. 优化分类筛选逻辑
    用filter替代map+push的低效写法,直接基于本地副本筛选:

    getCategories: (state, { payload }) => {
      const collectionId = payload;
      // 直接用filter筛选对应集合的分类
      state.value = categoryList.filter(item => item.collectionId === collectionId);
      state.currentCollectionId = collectionId;
    }
    
  3. 简化Redux状态更新
    Redux Toolkit内置的Immer允许直接修改state,无需手动做不可变更新,同时更新本地副本:

    addCategory: (state, { payload }) => {
      const newCategory = {
        collectionId: state.currentCollectionId,
        catId: RandomId(),
        name: payload.name,
        listEntries: payload.listEntries
      };
      // Immer会自动处理state的不可变更新
      state.value.push(newCategory);
      // 更新本地可修改的副本
      categoryList.push(newCategory);
    }
    
  4. 后续对接后端的优化建议
    当前本地副本方案仅作为过渡,正式对接后端后,建议将全量分类数据存储在Redux state中,通过createAsyncThunk发起异步请求拉取数据,所有增删改查操作都基于Redux state,不再依赖外部模块的数组变量,彻底避免模块冻结问题。

内容的提问来源于stack exchange,提问作者Matthew Ong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:10:02