以下是我的州树的简化版本:
{
"radius": 8,
"nodes": [
{ "id": 1, "x": 10, "y": 10 },
{ "id": 2, "x": 15, "y": 10 },
{ "id": 3, "x": 20, "y": 10 }
]
}
基本上,我有一个节点列表,每个节点都有 x 和 y 。我还有一个半径数,用于计算哪个其他节点在一个节点的半径范围内,即近邻。
我需要我的州看起来像这样:
{
"radius": 8,
"nodes": [
{ "id": 1, "x": 10, "y": 10, "neighbors": [2] },
{ "id": 2, "x": 15, "y": 10, "neighbors": [1, 3] },
{ "id": 3, "x": 20, "y": 10, "neighbors": [2] }
]
}
邻居的计算相当昂贵,所以如果其中一个节点位置发生变化,我真的只想计算它。
我查看了使用的选择器,但我不确定选择器是否有效。为了导出给定节点的邻居,我需要整个节点列表和半径。如果我将整个节点列表传递给选择器,则选择器将重新计算节点集合中的任何内容是否已更改。我只需要重新计算任何节点的x或y值是否已更改。请注意,除 x 和 y 之外,这些节点还有许多其他键。
似乎对于选择器工作,我需要一个新的选择器用于节点数组的每个元素,这是动态的。这是对的吗?
另一个障碍是我需要这个邻居列表来计算其他减速器中的其他状态。这是否意味着我应该在节点reducer中进行此计算?
有没有人对此问题有任何见解?
*修改
我最终将邻居移动到州而不是派生他们。对于某些其他操作,我需要从reducers中访问邻居,而我无法找到缓存/记忆它们的方法。
它似乎有点脆弱,因为我实际上是在某些涉及它们的行为中派生和存储邻居,而不是在其他不相关的行为上重新导出它们。 (这就是备忘选择器会自动执行的操作......)但是,我找不到一个好方法。
这是错的吗?
* EDIT2 我最终将节点状态分为两部分:
nodes: {
nodesById: {
"1": { "id": 1, "color": "blue", ... },
"2": { "id": 2, "color": "red", ... },
...
},
positionsById: {
"1": { "id": 1, "x": 0, "y": 10 },
"2": { "id": 2, "x": 10, "y": 10 },
...
}
}
这样,我就可以编写一个选择器,只接受节点的位置和半径来计算邻居:
export const getNeighborsById = createSelector(
(positionsById, radius) => positionsById,
(positionsById, radius) => radius,
(positionsById, radius) => {
// calculate neighbors
}
);
此选择器只会在positionsById
更改时重新运行,而不会在nodesById
更改时重新运行(发生更多)。
这解决了我的问题,但在同一个reducer中维护两个列表似乎有点不对,但可能不是......
答案 0 :(得分:2)
关于Redux中的状态有几种常见的误解:
根据您的使用案例,将您的节点存储到Array
似乎不是最佳选择。是的,您可以这样做,但无论何时添加,删除或修改节点,您都需要遍历列表并更新neighbors
。如果将此过程移动到选择器,则需要某种记忆而不是牺牲性能。
在我看来,您的节点应存储在空间分区数据结构中(例如,不可变的四叉树)。这种DS会使节点遍历速度更快,识别邻居也不会太难。
当您必须对州进行复杂选择或者在使用状态之前需要将状态导出为其他形状时,选择器非常有用。
如果您的节点存储在一个quardtree中,那么编写一个选择器来查询其邻居并将它们作为数组返回将很容易。
答案 1 :(得分:0)
本周我在我的应用程序中遇到了一个非常类似的问题,最后我在代表我的集合中每个项目的组件内部创建了memoized选择器。这样我就可以将每个项目的特定数据提供给每个组件中的Reselect.createSelector
,而不是尝试创建一个处理集合中所有元素的超级选择器。