Getting an error "A non-serializable value was detected in the state" when using redux toolkit - but NOT with normal redux
Olivia Zamora
I am trying to switch an app I am building over to use Redux Toolkit, and have noticed this error coming up as soon as I switched over to configureStore from createStore:
A non-serializable value was detected in the state, in the path: `varietals.red.0`. Value:, Varietal { "color": "red", "id": "2ada6486-b0b5-520e-b6ac-b91da6f1b901", "isCommon": true, "isSelected": false, "varietal": "bordeaux blend",
},
Take a look at the reducer(s) handling this action type: TOGGLE_VARIETAL.
(See )After poking around I found the issue seems to be with my custom models. For example the varietals array is created from a varietal model:
class Varietal { constructor(id, color, varietal, isSelected, isCommon) { this.id = id; this.color = color; this.varietal = varietal; this.isSelected = isSelected; this.isCommon = isCommon; }
}and using that I map over an array of strings to create my Varietal array which goes into my state:
// my utility function for creating the array
const createVarietalArray = (arr, color, isCommon) => arr.map(v => new Varietal(uuidv5(v, NAMESPACE), color, v, false, isCommon));';
// my array of strings
import redVarietals from '../constants/varietals/red';
// the final array to be exported and used in my state
export const COMMON_RED = createVarietalArray(redVarietals.common.sort(), 'red', true);When I switched out the model and replaced the array creating utility with something that returned a plain array of objects like this:
export const createVarietalArray = (arr, color, isCommon) => arr.map(v => ({ id: uuidv5(v, NAMESPACE), color, varietal: v, isSelected: false, isCommon, }));then that got the error to go away for that PARTICULAR reducer, however I have these custom models all through my app and before I start ripping them all out and recoding them simply to be able to use the Redux Toolkit I wanted to ask here if that is REALLY what the issue is before I did that...
114 Answers
This is more likely a problem from redux-persist. redux-toolkit provide few default middleware within it's getDefaultMiddleware
import { getDefaultMiddleware } from '@reduxjs/toolkit';You can disable each middleware by providing false flag. To remove serializableCheck
const customizedMiddleware = getDefaultMiddleware({ serializableCheck: false
})For details check redux-toolkit documentation.
[2021/10/07] edit:
To learn more about why this error happens, and why setting serializableCheck to false fixes it, read Working with Non-Serializable Data | Usage Guide redux toolkit docs.
Additionally, the getDefaultMiddleware export is being deprecated in favor of using a callback form. See Future planning: RTK 2.0? · Issue #958 · reduxjs/redux-toolkit and getDefaultMiddleware deprecated · Issue #1324 · reduxjs/redux-toolkit to learn more about why.
As to what it's being replaced with, see the getDefaultMiddleware | Redux Toolkit documentation to learn more:
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './reducer'
const store = configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
}) 6 Yes. We've always told Redux users they should not put non-serializable values in the store. Redux Toolkit was specifically designed to help provide good defaults when setting up a Redux store, and as part of that, it includes checks to make sure you're not accidentally mutating your data and that you're not including non-serializable values.
Class instances are by definition not fully serializable, so it's correctly flagging those instances as a problem. Please rewrite your logic to not pass those in to the store.
In general, React and Redux apps should be written using only plain JS objects and arrays as data. You don't need "model classes".
11With the getDefaultMiddleware getting depreciated, this can be resolved by using
middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: false, }), 2 I faced this issue on React Native when I wanted to implement redux-persist alongside @reduxjs/toolkit, and I got this error:
Then I fixed it by following code:
import { combineReducers, configureStore, getDefaultMiddleware,
} from '@reduxjs/toolkit';
import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER,
} from 'redux-persist';
import storage from 'utils/storage'; // in fact it is AsyncStorage
import counter from 'reduxStore/reducers';
const persistConfig = { key: 'root', storage,
};
const reducer = combineReducers({ counter,
});
const persistedReducer = persistReducer(persistConfig, reducer);
const store = configureStore({ reducer: persistedReducer, middleware: getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }),
});
export const persistor = persistStore(store);
export default store;Actually, these lines help me, without them I got above error:
middleware: getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], },
}), 7 This example shows excluding the serializable state check middleware enter link description here
const store = configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }),
}) Check the RTK docs on how to set it up with Redux Persist.
Basically it's just this line:
middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, })Which is in the configureStore call.
Also, you need this:
import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER,
} from 'redux-persist' 1 I added 'middleWare' parameter with 'serializableCheck: false,' problem solved. (it is not a problem actually)
source: link
import { configureStore } from '@reduxjs/toolkit' import rootReducer from './reducer' import { myCustomApiService } from './api' const store = configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }), }) I think the following is the best option if you know exactly what it is that is non-serializable but that you need in the reducer anyway:
const store = configureStore({ reducer: { reducer }, middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: { ignoredActions: ['TYPE'], ignoredActionPaths: ['property'], ignoredPaths: ['reducer.property'] } })
});For errors thrown in the action, either ignoredActions or ignoredActionPaths is enough. For errors thrown in the state, use ignoredPaths. Check the error message to see the names that you need to provide.
Just like @ReFruity , my issue appeared because I was keeping the AbortController there.
EDIT: Just an aside as, in order for the AbortController to work, I stored in the reducer just the signal and the abort method and I had to do this for it to work:
let abortMethod = abortController.abort.bind(abortController);Otherwise I was getting an error about illegal invocation.
One of the core usage principles for Redux is that you should not put non-serializable values in state or actions.
However, like most rules, there are exceptions. However, if you do need to turnoff those warnings, you can customize the middleware by configuring it to ignore specific action types, or fields in actions and state:
configureStore({ //... middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: ['your/action/type'], }, }),
})your/action/type should be replaced with actual action type which you will see in your warning message.
Reference:
I faced the kind of same error:
I was using the below versions and faced the same issue.
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.2",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"redux": "^4.2.1",
"redux-persist": "^6.0.0",I explored and got to know that the error message I am encountering suggests that there is a non-serializable value in an action object, I applied the below solution and it is working.
// store.js
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./reducers";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
const persistConfig = { key: "root", storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }),
});
const persistor = persistStore(store);
export { store, persistor }; I had the same problem and I solved it this way
you can just add a middleWare and pass it to configureStore
import { rootReducer } from "./root-reducer";
import { configureStore } from "@reduxjs/toolkit";
import logger from "redux-logger";
const middleWares = [logger];
export const store = configureStore({ reducer: rootReducer, middleware: middleWares,
}); getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }),import these flush purge etc from redux-persist and add the middleware to configure store! The issue would be solved!
If you are using axios in your createAsyncThunk method just return response,
createAsyncThunk( "post/getPost", async ({ id }) => { return axios .get(`) .then((res) => res); }
); As you know core use for Redux is that you should not put non-serializable values in state or actions.
But here we have an exceptions that there may be occasions when you have to deal with actions that need to accept non-serializable data but we need to make sure that these non-serializable payloads shouldn't ever make it into your application state through a reducer.
The serializability middleware will automatically warn anytime it detects non-serializable values in your actions or state. If you do need to turnoff those warnings, you can customize the middleware by configuring it to ignore specific action types, or fields in actions and state:
Note: It is recommended that to leave this middleware active to help avoid accidentally making mistakes.
configureStore({ //... middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { // Ignore these action types ignoredActions: ['your/action/type'], // Ignore these field paths in all actions ignoredActionPaths: ['meta.arg', 'payload.timestamp'], // Ignore these paths in the state ignoredPaths: ['items.dates'], }, }),
})More Detailed Explanation: