简体   繁体   中英

Using ES6 Map with React and/or Redux

I am looking at using the ES6 Map object to do my hashing for me, instead of creating a custom hashing function. However, it seems to me that it doesn't have much support for immutability, which is a key thing for React as well as Redux.

I am used to not using any libraries for immutability, but use just plain destructuring:

const newObj = { ...prevObj, newKey: "value" }

or map/filter with arrays.

However, ES6 Map has methods that directly update the object itself.

What I could think of was to do something like this:

var myMap = new Map()
Var myNewMap = { ...myMap.set() }
this.setState({ myMap:myNewMap })

But I am not sure if that would work.

All objects in JavaScript are reference types by nature ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#comparing_objects ). The only reason that the state object in Redux is immutable is because the library itself doesn't let you directly modify the state object return by getState .

Additionally, a JavaScript object is itself a dictionary, so fetching data from that state object by property name is still O(1) time complexity

If you just want a type-safe key-value store, you can use the following syntax:

entities: { [id: number]: MyEntity },
...

I thought about using Map data structure with react +redux recently and decided against it.

Though there is a slight performance benefit, for some use cases, in using the Map, mostly because the compiler knows better what to expect. And you get a size prop for free :)

I think there are more reasons agains this approach:

First of all redux doesn't like non serializable object - faq . it won't work if you want to use SSR or save your data in localStorage.

You can't enjoy many features in the redux toolkit, because they assume / create simple object data structures. for example the the createEntityAdapter - which I personally think is great

You can create a new map in each reducer, but it might get annoying pretty fast, and I'm not sure about the performance.

React doesn't care that much though :)

I am using an ES6 in React project that uses Redux as well.

use-case #1

Within a component:

I receive the data, then I need to process it, eg add some flags and/or additional parameters to each of the elements, then I store it within the component as ES6 Map(not in the component state).

Later when I need to update the data (eg new elements were fetched) I'm using Thunk that sets a loading flag in redux, which, later is back to false which triggers the update.

I update my ES6 Map with new data, mutating it, but not creating a new Map. My component gets updated because of the loading flag and re-renders with the new data from internal Map

use-case #2

In Redux state

I have an ES6 Map in my Redux state because I want to share it with multiple components. I keep it immutable like so in a reducer:

 const newMap = new Map(oldMap.entries())

but creating new Map on each update is only useful if you don't update it too often, but do often read from it.

In my case I have to use Map in Redux state because ES6 Map is keeping the order of elements while providing O(1) access to them.

So I'd suggest you to figure out parts where it's beneficial to use ES6 Map and where it's fine to just go along with regular Object .

It's actually very easy to use ES6's Map within the state. You need to use ReactDOM and ReactDOM.render(document.getElementById('someid'), map.get("123")); . But for this to have any advantageous benefits in terms of optimization, it's a bit more complicated. And it might only be useful for certain applications.

To avoid speaking strictly in the abstract, let's take a sample application: the DOM is an HTML <table> and the ReactJS state is a Map of coordinates and values. The <td> elements would have coordinate IDs, like id="pos-1,2" would be the position of the 1st row, the second column. Now the state could be looking like...

this.state = {
    'coordinates': Map({'1,2':'my 1/2 text!', '4,5':'my 4/5 text!'}),
};

To update state, you could have updateState() ...

updateState(newmap) {
    const coordinates = this.state.coordinates;
    newmap.keys().forEach((key) => {
        const parent = document.getElementById('pos' + key);
        const text = newmap.$key;
        ReactDOM.unmountComponentAtNode(parent);
        ReactDOM.render(parent, (<span>{text}</span>);
        coordinates.set(key, text);
    });
    this.state.setState({'coordinates':coordinates});
}

So, what is happening in here? We could update the state, whenever we want, with any position on the table grid. For instance, if we had 500 grids already with text in them, I could add just one more grid text field, with this.updateState(Map({'1000,500':'my text at position 1000x500!'})); . In this way, the Map() can be used within the state and it retains its benefit of an O(1) lookup for a single change, because our updateState() handles it.

This also bypasses much of the use of render() , but that is a design decision. I used the example of a 1,000x1,000 table grid, because having 1,000^2 unique, stateful elements has a tremendous load/handling time when using render() , and in that case, Map() would work just fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM