在React和/或Redux中使用ES6 Map

14

我打算使用 ES6 Map 对象来代替创建自定义哈希函数。然而,我发现它似乎不太支持不可变性,这是 React 和 Redux 的关键要素之一。

我通常不使用任何库来实现不可变性,而是只使用普通的解构:

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

或者使用数组的map/filter方法。

但是,ES6的Map提供了可以直接更新对象本身的方法。

我能想到的是像这样做:

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

但是我不确定那是否会奏效。


1
我会避免在Redux存储中使用Map。https://github.com/reduxjs/redux/issues/1499 - lecstor
10
好的,但是我接下来该如何获得相同的0(1)查找效益呢? - Vincent Rye
我猜唯一的问题是,您是否真正使用过普通对象而遇到了问题?如果没有,那么这可能是一种过早优化。如果有的话,您可能想看看一些替代Redux的选择,即使只针对有问题的数据。似乎让Maps在Redux中工作的复杂性不值得花费精力,甚至可能会抵消Maps的好处,例如创建新Maps比创建对象慢得多。在致力于大量工作之前进行一些真正的测量绝对值得。 - lecstor
基本上我同意你的看法。我当前的 redux 设置存在一些优化问题。于是我彻底断开了 Redux 的连接,回到了本地状态,这显然起作用了,但并没有解决我对 Redux 的基本问题。 我正在启动一个新项目,预计会遇到类似的问题,不过这是一个比较大的项目,使用 Redux 会有一些好处,所以我想提前做好充分的研究。同时我也在研究 reselect,这应该能够解决其中的一部分问题。 - Vincent Rye
优化您的选择器似乎是正确的方法。还有re-reselect,它增加了更多的灵活性。*虽然我还没有使用过。这是一篇我刚刚发现的好文章.. https://medium.com/riipen-engineering/be-selective-with-your-state-8f1be76cb9f4 - lecstor
显示剩余4条评论
5个回答

4
如果您只需要一个类型安全的键值存储,可以使用以下语法:
entities: { [id: number]: MyEntity },
...

4

最近我考虑在react + redux中使用Map数据结构,但最终决定不采用。

虽然使用Map可能会带来轻微的性能提升,对于某些用例而言,主要是因为编译器更了解预期结果。并且你可以免费获得一个size属性 :)

我认为有更多的理由反对这种方法:

首先,redux不喜欢非可序列化对象 - faq。如果你想使用SSR或在localStorage中保存数据,它将无法工作。

你无法享受到redux toolkit中的许多功能,因为它们假设/创建简单的对象数据结构。例如createEntityAdapter - 我个人认为这很棒

你可以在每个reducer中创建一个新的map,但这可能会变得非常烦人,而且我不确定性能如何。

不过React并不太关心这个问题 :)


0

-2

我正在使用React项目中的ES6和Redux。

用例#1

在组件内部:

我先接收数据,然后需要处理它们,例如为每个元素添加一些标记和/或其他参数,然后将其存储在组件内部作为ES6 Map(而不是组件状态)。

稍后,当我需要更新数据时(例如获取了新元素),我使用Thunk在Redux中设置一个loading标志,这个标志后来会变为false,从而触发更新。

我利用更新后的数据对我的ES6 Map进行改变,但不创建新的Map。由于loading标志,我的组件会得到更新,并使用来自内部Map的新数据重新渲染。

用例#2

在Redux状态中

我在Redux状态中有一个ES6 Map,因为我想与多个组件共享它。我在reducer中保持它的不可变性,例如:

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

但是在每次更新时创建新的Map只有在您不经常更新它,但经常从中读取时才有用。

在我的情况下,我必须在Redux状态中使用Map,因为ES6 Map保留元素的顺序,同时提供O(1)访问它们。

因此,我建议您找出何时使用ES6 Map有益,何时可以使用常规Object


在我的情况下,我必须在Redux状态中使用Map,因为ES6 Map保持元素的顺序,同时提供O(1)访问它们的能力。解决这个问题的常见方法是使用简单的带有键的数组来维护顺序,并使用对象将键映射到具有恒定查找时间的值。是的,它稍微有些冗余,因为键在数组和对象中重复出现,但它更适合于基于原始数据类型且可序列化的redux存储的思想。您可以编写一个选择器,以便方便地按顺序迭代键/值对。 - timotgl

-4

在状态中使用ES6的Map非常容易。您需要使用ReactDOMReactDOM.render(document.getElementById('someid'), map.get("123"));。但是,为了在优化方面获得任何有利的好处,这需要更复杂一些。它可能只对某些应用程序有用。

为了避免严格地抽象讲话,让我们来看一个示例应用程序:DOM是HTML <table>,而ReactJS状态是坐标和值的Map<td>元素将具有坐标ID,例如id="pos-1,2"将是第一行第二列的位置。现在状态可能看起来像...

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

要更新状态,你可以使用 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});
}

那么,这里发生了什么?我们可以随时使用表格网格上的任何位置更新状态。例如,如果我们已经有500个带有文本的网格,我可以添加一个网格文本字段,并使用this.updateState(Map({'1000,500':'my text at position 1000x500!'}));。通过这种方式,Map()可以在状态中使用,并保留其单个更改的O(1)查找优势,因为我们的updateState()处理它。

这也绕过了大部分render()的使用,但这是一个设计决策。我以1,000x1,000表格网格为例,因为当使用render()时,拥有1,000^2个唯一的、有状态的元素会产生巨大的负载/处理时间,在这种情况下,Map()将工作得很好。


1
我认为有一些误解。这个问题是关于Map对象,但是这个答案是关于Array.prototype.map()函数。虽然名字相似,但概念不同。 - Kostas
嘿,Kostas:感谢你提醒我,我已经将一些小写字母改为 Map() 的大写字母,现在应该已经修复了。 - HoldOffHunger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接