0

In general, using a mutable object such as Map is strongly discouraged.

However, the magic of immer allows immutable objects to be manipulated as though they are mutable.

Specifically, immer supports immutable versions of Map using enableMapSet

In redux-toolkit createReducer and createSlice wrap state manipulation with immer's produce.

Collectively, I think these facts mean code like this should be safe:

import { createSlice } from '@reduxjs/toolkit'

export const testmapSlice = createSlice({
  name: 'testMap',
  // Using a Map() as redux state
  initialState: new Map(),
  reducers: {
    add: (state, action) => {
      state.set(action.payload.identity, action.payload)
    },
  },
})

However, when I use this in a React component, I get the polite error message A non-serializable value was detected in the state, in the path: `testMap`. Value: Map(1) {"A" => {…}} Take a look at the reducer(s) handling this action type: testMap/add..

Is there a way to safely use Map without getting this error message?

1 Answers1

0

Define "safely" :)

In theory, you could put anything you want into the Redux store.

In practice, per that FAQ, non-serializable values are likely to cause things like the DevTools to break (which defeats much of the purpose of using Redux in the first place). Use of Maps and other mutable instances are also likely to cause portions of your UI to not re-render correctly, because React-Redux relies on reference checks to determine if data has changed. So, we specifically tell users that you should never put non-serializable values in the Redux state.

In this particular case, you should be able to use a plain JS object as a lookup table rather than a Map, and accomplish the same behavior.

As an absolute last resort, you can turn off the serialization checking for certain parts of the state, but we strongly discourage folks from doing that.

markerikson
  • 42,022
  • 7
  • 87
  • 109
  • 1
    I'm specifically curious about whether immer changes anything. If I was using immutableJS, I could set the initial value to a Map and know it was immutable and that redux is happy. But with immer being the redux-toolkit default, I wondered if there's a way to tell redux, "this is frozen by immer" in the initial state. For my use case, the insertion order iteration of map is desirable, but the number of keys is low, so I'll probably use an array instead. – Dusty Phillips Jul 22 '20 at 16:48
  • Tbh I don't know exactly how Immer implements support for updating `Map`s. My assumption is that it doesn't copy the maps, which would lead to bugs if you try to read `state => state.some.map` in the UI - it would never know to re-render. Note that JS objects [do _mostly_ use insertion order for iteration of string keys](https://stackoverflow.com/a/5525820/62937), which may be sufficient for your purposes. – markerikson Jul 22 '20 at 17:02