'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 |