Getting an error "A non-serializable value was detected in the state" when using redux toolkit - but NOT with normal redux
Asked Answered
S

16

200

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 https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)

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

Soche answered 9/5, 2020 at 22:47 Comment(1)
In my case, I was passing a full network response to redux instead of response.dataTelevise
P
180

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(),
})
Primm answered 4/8, 2020 at 10:20 Comment(6)
Anyone seeing this and working with redux-persist, do not entirely disable the serializable check unless you know what you're doing. It's better to specifically disable the actions from redux-persist. See the answer from @AmerllicALynda
In my case it is probably caused by redux-saga: A non-serializable value was detected in an action, in the path: type. Value: ƒ actionCreator() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (prepareAction) Option above suppressed the error but I don't think it is correct workaround.Sligo
getDefaultMiddleware is now deprecated, see answer https://mcmap.net/q/127372/-getting-an-error-quot-a-non-serializable-value-was-detected-in-the-state-quot-when-using-redux-toolkit-but-not-with-normal-redux from boluwatife-fakoredeMontsaintmichel
Thanks for the heads up @DakoJunior, edited the answer to include links to why it's being deprecated, and how to use the new callback syntax.Infighting
Is it ok to put instances of AbortController in the redux store?Conductor
If someone is encountering this issue in the context of redux-persist, this is how you do it: redux-toolkit.js.org/usage/usage-guide#use-with-redux-persistRebarebah
R
86

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".

Rathbone answered 10/5, 2020 at 1:48 Comment(11)
Um. I have no idea why you felt the need to drop this comment, on an answer I wrote a year ago. Ultimately it's your codebase, and you can do whatever you want with it. But as a general rule, React ecosystem apps don't use model classes, and Redux has always been very opinionated about not allowing classes in the Redux store state. So, that's the advice we give to people.Rathbone
Wouldn't be nice to add a way to control the serialization and thus allowing to store every value type we want and/or need?Biltong
You can customize the behavior of the serialization check middleware if desired, including overriding the "what values are serializable?" callback or turning it off entirely. However, we strongly recommend that you do not do that. Ultimately it's your app and you can do whatever you want, but these guidelines exist for a reason.Rathbone
Thanks! Already seen this, and thought it wasn't what I need: I expected to find a way to convert my classes to/from string. Am I missing something?Biltong
Uh... no, we don't do any serialization of values ourselves. Redux state is ultimately "just" a normal JS variable. But, viewing Redux state via the DevTools requires that the state be serialized out to the extension, and cases like persistence require the state to be serialized as well. Additionally, storing class instances implies mutable state updates, which are not allowed in Redux. So, we're trying to block people from making that sort of mistake. I'd strongly suggest that you try to find a different approach to working with your data in Redux.Rathbone
And given a large (I mean very large) set of typescript classes already in place that we want to use as state and that have many properties of Date type, which approach would you recommend?Biltong
In all honesty, in that case you may not want to use Redux :)Rathbone
To add to what Mark said regarding how to solve this by overriding Redux's serialization behavior, the Redux Toolkit docs on Working with Non-Serializable Data are a great read that further explains why this issue happens, and how to apply a fix to the specific part of your redux application that is causing it: redux-toolkit.js.org/usage/…Infighting
JavaScript "Set" is still not recognized and shows this warning all the time. I think we can now extend this to include Set and other new data structures apart from arrays.Jarredjarrell
Correct. Map, Set, Date, and classes are all considered "non-serializable values" by default.Rathbone
@Rathbone Thank you, i don't understand until now why i got this error! and because of the explanation you wrote, now i understand very well! so thx.Gentlemanatarms
H
86

With the getDefaultMiddleware getting depreciated, this can be resolved by using

   middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
Herbal answered 24/7, 2021 at 11:52 Comment(2)
Can we do this serializableCheck false for only one reducer?Subaquatic
This works, however, what happens If I make it serializableCheck: false,Limeade
C
55

I faced this issue on React Native when I wanted to implement redux-persist alongside @reduxjs/toolkit, and I got this error:

enter image description here

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],
  },
}),
Clothbound answered 27/6, 2020 at 13:8 Comment(7)
This is consistent with the RTK docs: redux-toolkit.js.org/usage/usage-guide#use-with-redux-persistNitrogenize
I did the same thing but i get the same error.Acceptant
@Dijiflex, I hope and pray your issue will be settled.Clothbound
@Acceptant same for me even after I re-start my local server, but somehow the warnings disappeared after several trials of restart.Forbore
Awesome, thanks @AmerllicA!! Sounds better than skipping all the actions with serializableCheck: falseClique
This should definitely be the accepted answer for this issue. serializableCheck: false is just a bandaid. Instead, passing only the specific actions used by redux-persist is consistent with the redux toolkit docs and the correct (and specific) fix to the warning.N
The FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, and REGISTER action types are all related to the redux-persist library, which is used to persist your Redux state between sessions. These actions are used internally by redux-persist and do not need to be serializable, which is why they are included in the ignoredActions array.Domiciliate
B
33

This example shows excluding the serializable state check middleware enter link description here

 const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
})
Batson answered 21/4, 2022 at 13:46 Comment(0)
F
16

Check the RTK docs on how to set it up with Redux Persist.

https://redux-toolkit.js.org/usage/usage-guide#use-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'
Firm answered 1/9, 2022 at 15:57 Comment(1)
And combining it with this another solution is even better github.com/vercel/next.js/discussions/…Dannica
W
8

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,
        }),
    })
Wolframite answered 16/5, 2022 at 12:49 Comment(0)
R
4

This typically happens when you try to include non-serializable values, such as functions, in your Redux actions. Redux Toolkit's configureStore function includes a middleware

Add middleware:

export const store = configureStore({
    reducer: persistedReducer,
    middleware: getDefaultMiddleware =>
        getDefaultMiddleware({
            serializableCheck: false,
        }),
});
Repeal answered 11/3 at 14:37 Comment(2)
this makes the error disappear! but don't we need the sereailizable check given that redux expects serializable objects?Sulfonate
Disabling the serializableCheck middleware in Redux Toolkit eliminates errors related to non-serializable values in actions. Yes you are right, it's generally advisable to maintain serializability for Redux actions to ensure compatibility and safety. If non-serializable values are necessary, then selectively disable the check for specific actions or payloads using the ignoredActionPaths option inside the serializableCheck. serializableCheck: { ignoredActionPaths: ['some/action/path'], }, `Repeal
T
3

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.

Tezel answered 26/5, 2022 at 18:20 Comment(0)
B
3

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: https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data

Beet answered 12/9, 2022 at 18:37 Comment(0)
F
3

I faced the kind of same error:

enter image description here

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 };
Fault answered 8/10, 2023 at 17:31 Comment(0)
J
1

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:

  1. https://redux-toolkit.js.org/usage/usage-guide#working-with-non-serializable-data
  2. https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants
Jobber answered 11/3, 2023 at 6:57 Comment(0)
S
0

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,
});
Sennight answered 3/7, 2022 at 7:50 Comment(0)
S
0
    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!

Stlaurent answered 25/9, 2022 at 5:7 Comment(0)
N
0

If you are using axios in your createAsyncThunk method just return response,

createAsyncThunk(
  "post/getPost",
  async ({ id }) => {
    return axios
      .get(`https://jsonplaceholder.typicode.com/posts/${id}`)
      .then((res) => res);
  }
);
Nascent answered 31/12, 2022 at 4:6 Comment(0)
T
0

Instead of using Typescripts classes with methods in it, you must use Javascript objects. This means:

Instead of using this:

const track1 = new Track("7empest", "TOOL", "Fear Inoculum", new Date(2024, 3, 2), 60 * 3);

You must use:

const track1 = { 
    title: "7empest", 
    artist: "TOOL", 
    album: "Fear Inoculum", 
    releaseDate: new Date(2024, 3, 2), 
    durationInSeconds: 60 * 3 
};
Transpontine answered 26/3 at 21:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.