以下是我状态树的简化版本:
{
"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之外还有很多其他键。
此外,似乎要使选择器工作,我需要为节点数组的每个元素创建一个新的选择器,而该数组是动态的。这是正确的吗?
另一个障碍是,我需要此邻居列表来计算其他reducers中的其他状态。这是否意味着我应该在节点reducer中进行此计算?
有人对这个问题有什么见解吗?
*编辑
最终我把邻居移到了状态中而不是推导它们。我需要从其他操作中访问邻居,并且我无法找到缓存/记忆它们的方法。
这似乎有点脆弱,因为我实际上是在某些涉及它们的操作中派生和存储邻居,并且在其他不涉及它们的操作中不重新派生它们。 (这几乎就是一个自动执行记忆化选择器的过程...)但遗憾的是,我找不到一个好的方法来做到这一点。
这样做有问题吗?
*编辑2
最终我将我的节点状态分为两部分:
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中维护两个列表似乎有点不对,但也许不是...