I’m building a Redux store using Redux Toolkit and I want to store a map of users in my state, like this:
export type UserType = {
_id: string
firstName: string;
lastName: string;
email: string;
};
interface UserStoreType {
users: Map<string, UserType>
}
const initialState: UserStoreType = {
users: new Map();
};
export const userSlice = createSlice({
name: "counter",
initialState,
reducers: {
append: (state, action) => {
const user = action.payload;
state.users.set(user._id, user);
}
},
});
export const userAction = userSlice.actions;
export const userReducer = userSlice.reducer;
However, I’ve seen some mentions that using Map or Set in Redux state is an anti-pattern and should be avoided.
Is it considered bad practice to use Map/Set in my Redux state like this? If so, what is the recommended approach? Should I just use a plain JavaScript object instead of a Map?
I’m using Redux Toolkit, so I know I can safely mutate the state in reducers. But I’m not sure if using Map/Set specifically is problematic or discouraged for any reasons.
Any guidance on best practices here would be appreciated!
I tried implementing my Redux state using a Map like in the code example above. I was expecting that this would allow me to efficiently look up users by ID.
The main reason I opted for a Map over a plain JavaScript object is because I thought it would provide better performance for lookups compared to searching through an object by key.
I also liked the API that Map provides, such as being able to iterate through keys/values easily.
My expectation was that using Map would be a reasonable approach for storing key/value pairs in my Redux state.
However, after reading some discussions online, it seems using Map/Set might not be recommended. So I wanted to get some clarity around if I should avoid using Map/Set or if it’s actually ok to use them in this case.
Let me know if any other details would be helpful! I’m open to changing my implementation if there are good reasons not to use Map/Set here.
2
Answers
Using Maps/Sets is already not recommended by the Redux maintainers. See the Redux style-guide/best-practices, specifically Do Not Put Non-Serializable Values in State or Actions listed as "essential", meaning:
Using plain Javascript objects also provides O(1), e.g. constant time, lookup using property accessors, e.g.
someObject[someKey]
. It’s quite common to create a "Map lookup" object for just this purpose.Redux-Toolkit uses Immer.js under the hood, so in order to use
Map
/Set
you’ll need to do extra step to enable their usage.Additionally, React works on shallow reference equality checks, so any state updates necessarily need to create new references. RTK (*and immer) abstracts this away which allows you to write mutable state updates and it handles creating the new references under the hood. I don’t have performance specs but shallow copying is at least an O(n) operation in both cases (
Map
/Set
vs JS Object) to copy each key/value.Even after you’ve enabled
Map
/Set
to be used in RTK the Redux-Devtools are not as helpful. The state updates correctly but you don’t have direct insight into the value.After all the above setup you’ll still see an error in the console regarding serializability.
You can modify the store to ignore this specific part of state so the error is not produced. See Serializability Middleware.
As your state becomes more complex or you later decide you would like to persist your Redux store, you’ll again likely run into the issue/problem of serializability. Most solutions involve serializing the state to JSON for longer-term storage like localStorage.
Map
objects just don’t serialize well out-of-the-box. Plain Javascript objects are just easier to work with.Conclusion
For all the reasons above I’d say yeah, using
Map
/Set
in Redux state is a poor decision and should not be used. Just about any performance gain you get from using aMap
/Set
is IMHO easily wiped out by all the extra setup/configuration/logic necessary to make working with them easier.The Redux docs advises against using
Map
,Set
, or any non-plain Objects in Redux state management. They recommend using plain JS objects and arrays as they are easier to handle with existing tools and practices in the Redux ecosystem. This makes the state easier to manage and serialize. It also adheres to Immer’s immutability practices.Here are the primary reasons:
Serialization and debugging
Redux state is typically serialized (converted to a string format) when using tools like the Redux DevTools for debugging. JavaScript’s
Map
andSet
types are not easily serializable to JSON without custom logic, asJSON.stringify()
does not correctly handleMap
andSet
. This can lead to difficulties when trying to inspect the state in developer tools or persisting the state in storage that only supports string data, likelocalStorage
.Immutability concerns
Redux relies heavily on immutability for efficient updates, especially when connected with React or similar libraries. While it’s possible to treat
Map
andSet
objects immutably (by always creating a newMap
orSet
with changes rather than modifying the original), common methods for these types like.set()
and.delete()
mutate the object in place. This mutation can cause subtle bugs in Redux, particularly when components do not re-render in response to state changes because Redux did not detect any changes (due to direct mutation).