这是我的状态树的简化版本:
{
"radius": 8,
"nodes": [
{ "id": 1, "x": 10, "y": 10 },
{ "id": 2, "x": 15, "y": 10 },
{ "id": 3, "x": 20, "y": 10 }
]
}
Run Code Online (Sandbox Code Playgroud)
基本上,我有一个节点列表,每个节点都有一个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] }
]
}
Run Code Online (Sandbox Code Playgroud)
邻居的计算相当昂贵,所以如果其中一个节点位置发生变化,我真的只想计算它.
我查看了使用的选择器,但我不确定选择器是否有效.为了导出给定节点的邻居,我需要整个节点列表和半径.如果我将整个节点列表传递给选择器,则选择器将重新计算节点集合中的任何内容是否已更改.我只需要重新计算任何节点的x或y值是否已更改.请注意,除了x和y之外,这些节点还有许多其他键.
似乎对于选择器工作,我需要一个新的选择器用于节点数组的每个元素,这是动态的.它是否正确?
另一个障碍是我需要这个邻居列表来计算其他减速器中的其他状态.这是否意味着我应该在节点减速器中进行此计算?
有没有人对这个问题有任何见解?
*编辑
我最终将邻居移动到州而不是派生他们.对于某些其他操作,我需要从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 },
...
}
}
Run Code Online (Sandbox Code Playgroud)
这样,我就可以编写一个选择器,它只接受节点的位置和半径来计算邻居:
export const getNeighborsById = createSelector(
(positionsById, radius) => positionsById,
(positionsById, radius) => radius,
(positionsById, radius) => {
// calculate neighbors
}
);
Run Code Online (Sandbox Code Playgroud)
此选择器仅在positionsById更改时重新运行,而不在nodesById更改时重新运行(发生更多).
这解决了我的问题,但在同一个reducer中维护两个列表似乎有点不对,但可能不是......
关于 Redux 中的状态有几个常见的误解:
根据您的用例,将节点存储到 an 中Array似乎不是最佳选择。是的,您可以这样做,但是每当添加、删除或修改节点时,您都需要遍历列表并更新neighbors。如果将此过程移至选择器,则需要某种记忆而不牺牲性能。
在我看来,您的节点应该存储在空间分区数据结构中(例如不可变的四叉树)。这种 DS 将使节点遍历速度更快,并且识别邻居应该不会太难。
当您必须对状态执行复杂的选择或者需要在使用状态之前将其导出为另一种形状时,选择器非常有用。
如果您的节点存储在四叉树中,则可以轻松编写一个选择器来查询其邻居并将它们作为数组返回。