'TypeError: undefined is not an object (evaluating 'state.selectedData.push') - Can't add payload to state using Redux Toolkit

I'm having an issue using Redux Toolkit. I want to push an object to my initial state called selectedData which is an array, this gives me the error "TypeError: undefined is not an object (evaluating 'state.selectedData.push')".

I've been following https://redux-toolkit.js.org/usage/immer-reducers and they literally write their reducer logic the same as mine. I have no idea why it doesn't work for me.

Their code example:

const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    todoAdded(state, action) {
      // "Mutate" the existing state, no return value needed
      state.push(action.payload)
    },
    todoDeleted(state, action.payload) {
      // Construct a new result array immutably and return it
      return state.filter(todo => todo.id !== action.payload)
    }
  }
})

My code


import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";


export const exerciseItemSlice = createSlice({
    name: "exerciseItem",
    initialState: { selectedData: [] },
    reducers: {
        saveSelectedItemIdsToRedux: (state, action) => {
            state.selectedData.push(action.payload);

        },
        resetSelectedItemIds(state, action) {
            return state.itemSelectedData;
        }

    },

}


);

// Action creators are generated for each case reducer function
export const { saveSelectedItemIdsToRedux, resetSelectedItemIds } = exerciseItemSlice.actions;


export default exerciseItemSlice.reducer;


The only difference is that I'm passing a state called selectedData, I have tried with initialState only as well.

EDIT: Updating with all relevant code as the issue can't be reproduced with above code, indicating the issue should be somewhere else?

rootReducer.js

import counterReducer from "./counterSlice";
import texterReducer from "./testSlice";
import setsTextReducer from "./setsTextSlicer";
import templateReducer from "./templateSlice";
import setsScreenReducer from "./setsScreenSlice";
import exerciseBrowserReducer from "./exerciseBrowserSlice";
import exerciseItemReducer from "./exerciseItemSlice";
import { combineReducers } from "redux";


const rootReducer = combineReducers({
    counter: counterReducer,
    texter: texterReducer,
    setsText: setsTextReducer,
    template: templateReducer,
    setsScreen: setsScreenReducer,
    exercise: exerciseBrowserReducer,
    exerciseItem: exerciseItemReducer,
});

export default rootReducer;

store.js

import { configureStore } from "@reduxjs/toolkit";

import {
    persistStore,
    persistReducer,
    FLUSH,
    REHYDRATE,
    PAUSE,
    PERSIST,
    PURGE,
    REGISTER,
} from 'redux-persist'
import AsyncStorage from "@react-native-async-storage/async-storage";

import rootReducer from "./rootReducer";

const persistConfig = {
    key: 'root',
    version: 1,
    storage: AsyncStorage,
    //blacklist: [rootReducer.exerciseItem]
};



const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
    reducer: persistedReducer,

    // https://github.com/reduxjs/redux-thunk
    middleware: getDefaultMiddleware =>
        getDefaultMiddleware({
            serializableCheck: {
                ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
            },

        })
});

export const persistor = persistStore(store);
export { store };

UPDATE

I did the good old remove and re-add and now it's working. Renamed the slice/reducer as it had the same name as another component(Feels weird but that might have been the issue?). I've compared my old code to the now working and I can't find any difference, so my best bet would be that the renaming was the issue.



Solution 1:[1]

Redux persists serialize (converting to string) your state data in order saved in Async Storage. After serialization, you can't access the array method including Array.push.

Try to parse the array before using the array method.

 saveSelectedItemIdsToRedux: (state, action) => {

const serializedData = JSON.parse(state.selectedData)

state.selectedData = serializedData.push(action.payload);

        }


Note: JSON.parse sometimes fails when you work with an array with nested objects.

Solution 2:[2]

Hope this helps! Remove arrow function and use it like

export const exerciseItemSlice = createSlice({
    name: "exerciseItem",
    initialState: { selectedData: [] },
    reducers: {
        // below line is the change
        saveSelectedItemIdsToRedux(state, action){
            state.selectedData.push(action.payload);

        },
        resetSelectedItemIds(state, action) {
            return state.itemSelectedData;
        }

    },

}

Solution 3:[3]

I had the same issue and figured out this: With redux-toolkit, each reducer owns its slice of state, so at the time you call state in

 state.selectedData.push(action.payload);

you're in fact calling State => selectedData => selectedData whitch is undefined

For use your selectedData inside a reducer's function with the createSlice API just call state and it means State => selectedData.

then you must change from this:

reducers: {
        saveSelectedItemIdsToRedux: (state, action) => {
            state.selectedData.push(action.payload);

        },
        resetSelectedItemIds(state, action) {
            return state.itemSelectedData;
        }

    },

to this

reducers: {
        saveSelectedItemIdsToRedux: (state, action) => {
            state.push(action.payload);

        },
        resetSelectedItemIds(state, action) {
            return state;
        }

    },

and it will work :-D

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 BYIRINGIRO Emmanuel
Solution 2 Basava
Solution 3 Guillermo Mata