Search code examples
javascriptreactjsarraysreduxredux-toolkit

Uncaught TypeError: Cannot read properties of undefined (reading 'push'), I'm getting error while using addToCart


    import { createSlice } from '@reduxjs/toolkit';

    const cartSlice = createSlice({
    name: 'cart',
    initialState: {
    items: [],
    },
    reducers: {
    addToCart: (state, action) => {
      state.items.push(action.payload);
    },
    removeFromCart: (state, action) => {
      state.items = state.items.filter(item => item.id !== action.payload.id);
    },
    clearCart: state => {
      state.items = [];
    },
    },
    });

export const { addToCart, removeFromCart, clearCart } = cartSlice.actions;

export default cartSlice.reducer;

It's working while I defined initialstate as array (initialstate =[]) and performing operation (state.push(...)).


Solution

  • Issue

    The issue here it seems is that you once had a cart slice the likely looked like the following:

    const initialState = [];
    
    const cartSlice = createSlice({
      name: "cart",
      initialState,
      reducers: {
        addToCart: (state, action) => {
          state.push(action.payload);
        },
        removeFromCart: (state, action) => {
          return state.filter((item) => item.id !== action.payload.id);
        },
        clearCart: (state) => {
          return [];
        }
      }
    });
    

    The state this slice/reducer produced was persisted to localStorage.

    Example:

    "persist:root": "{"cart":"[{\"id\":\"_DX4ebWHrqY1R6iVevCLV\"},{\"id\":\"JTqqLWDHTyCbk4gSroFYX\"}]","_persist":"{\"version\":-1,\"rehydrated\":true}"}"
    

    You then changed the cart slice reducers to generate a different state shape:

    const initialState = {
      items: []
    };
    
    const cartSlice = createSlice({
      name: "cart",
      initialState,
      reducers: {
        addToCart: (state, action) => {
          state.items.push(action.payload);
        },
        removeFromCart: (state, action) => {
          state.items = state.items.filter((item) => item.id !== action.payload.id);
        },
        clearCart: (state) => {
          state.items = [];
        }
      }
    });
    

    But the previous state shape still exists in localStorage and is rehydrated as the state value, and since there is now a mismatch between the current state value and what the reducers/selectors reference this creates the errors.

    Solution(s)

    Reset the saved persisted state

    If you don't care or need to save the existing state then you can simply delete the persist:root key/value from localStorage and reload the app. Since there will now be no persisted state to hydrate the app state the initial state from the reducers will compute the initial state tree with the current initial state values.

    • Open the browser's dev tools
    • Navigate to the "Application" tab
    • Locate the localStorage section
    • Delete the "persist:root" entry
    • Reload the app

    enter image description here

    Migrate the old state to the new state

    Create a migration from the old, incorrect, state to the new current state shape.

    import { configureStore, combineReducers } from "@reduxjs/toolkit";
    import { createMigrate, persistStore, persistReducer } from "redux-persist";
    import storage from "redux-persist/lib/storage";
    import cartReducer from "./cart.slice";
    
    const migrations = {
      // Move state.cart to state.cart.items
      0: (state) => {
        const { cart, ...newState } = state;
        return {
          ...newState,
          cart: {
            items: cart
          }
        };
      }
    };
    
    const rootReducer = combineReducers({
      cart: cartReducer
      auth: authReducer
    });
    
    const persistConfig = {
      key: "root",
      storage,
      version: 0, // <-- set state version
      migrate: createMigrate(migrations, { debug: true }) // <-- debug true if you want to see logs, otherwise set false
    };
    
    const persistedReducer = persistReducer(persistConfig, rootReducer);
    
    const store = configureStore({
      reducer: persistedReducer
    });
    
    const persistor = persistStore(store);
    
    export { store, persistor };