28

This is more of a general, best-practices question.

I've been playing around here and there with JavaScript Maps and have been trying to find more info on whether or not it's considered an anti-pattern/code smell to set state properties to a Map. The link below is an issue thread in the Redux repo with some comments such as:

"You can use Maps and Sets as state values, but it's not recommended due to serializability concerns."

However this thread is about Redux. What about vanilla React? Anyone have any strong opinions or insight? Sorry if this question is in the wrong place.

https://github.com/reduxjs/redux/issues/1499

Jose
  • 4,880
  • 8
  • 27
  • 49
  • 2
    This is an interesting question - but is probably more on topic at https://softwareengineering.stackexchange.com/, also - could you post an example of what you're meaning? – dwjohnston Dec 04 '18 at 04:39
  • The creator of Redux said that React state is the same as Redux store, it's recommended it be serializable, though it's not a requirement. – Jayce444 Dec 04 '18 at 04:42
  • @dwjohnston I don't really have an example, it was just something I was wondering about. I could give a generic, contrived example but it would just be a setState call like so: this.setState({ map: new Map() }) – Jose Dec 04 '18 at 04:45

2 Answers2

30

If you don't want to use an immutable library you can create a new Map on change (this is a shallow copy of the map, like spreading an object):

const [someMap, setSomeMap] = useState(new Map());

And when you need to update it:

setSomeMap(new Map(someMap.set('someKey', 'a new value')));

The same concept applies to Redux:

case 'SomeAction':
  return {
    ...state,
    yourMap: new Map(state.yourMap.set('someKey', 'a new value'))
  }

With regards to serializability it's not a concern for local state. It's good practice to have a Redux store that is serializable though.

Can I put functions, promises, or other non-serializable items in my store state?

It is highly recommended that you only put plain serializable objects, arrays, and primitives into your store. It's technically possible to insert non-serializable items into the store, but doing so can break the ability to persist and rehydrate the contents of a store, as well as interfere with time-travel debugging.

If you are okay with things like persistence and time-travel debugging potentially not working as intended, then you are totally welcome to put non-serializable items into your Redux store. Ultimately, it's your application, and how you implement it is up to you. As with many other things about Redux, just be sure you understand what tradeoffs are involved.

You can see that JSON.stringify unfortunately doesn't work on maps:

console.log(JSON.stringify(
  new Map([['key1', 'value1'], ['key2', 'value2']])
))

If you can get in between the serialization process you can use Array.from:

console.log(JSON.stringify(
  Array.from(new Map([['key1', 'value1'], ['key2', 'value2']]))
))
Dominic
  • 62,658
  • 20
  • 139
  • 163
25

React state should be immutable because React uses shallow compare to check for equality. When comparing scalar values (numbers, strings) it compares their values. When comparing objects, it does not compare their properties - only their references are compared (i.e. "do they point to same object ?").

ES6 Maps are not immutable and are optimized for mutability, that's why it's not recommended to use these in React as it is. React will not know whether map is updated or not.

var map1 = new Map();
var map2 = map1.set('b', 2); // mutate map
map1 === map2; // true because reference remains unchanged after mutation

You can use Maps if you want but you need to use some immutability helper e.g. Immutable.js. Following example is using immutable map

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 2); // Set to same value
map1 === map2; // true
const map3 = map1.set('b', 4); // Set to different value
map1 === map3; // false

References:

https://github.com/reduxjs/redux/issues/1499#issuecomment-194002599

https://stackoverflow.com/a/36084891/2073920

ssc
  • 9,528
  • 10
  • 64
  • 94
Abdul Rauf
  • 5,798
  • 5
  • 50
  • 70