问题描述
我正在开发一个React / Redux应用程序,它允许将小部件添加到页面并在2D空间中进行操作。需要一次选择和操作多个小部件。我当前状态树的简化版本如下所示...
{
widgets:{
widget_1:{x:100,y:200},
widget_2:{x:300,y:400},
widget_3:{x:500,y:600}
} ,
选择:{
小部件:[widget_1,widget_3]
}
}
我目前有这个树由2个reducer管理,一个管理小部件
状态,另一个管理选择
州。选择状态reducer可以简化为(注意:我也使用Immutable.js)...
currentSelectedWidgets(state = EMPTY_SELECTION,action){
switch(action.type){
case SET_SELECTION:
return state.set('widgets',List(action.ids));
默认值:
返回状态
}
}
这一切看起来效果都不错,但我现在要求我努力适应这个模型...
出于用户界面的目的,我需要了解当前的情况选择具有x / y属性,该属性反映当前所选小部件x / y坐标的左上角。示例状态可能看起来像......
{
widgets:{
widget_1:{x: 100,y:200},
widget_2:{x:300,y:400},
widget_3:{x:500,y:600}
},
选择: {
x:100,
y:200,
小部件:[widget_1,widget_3]
}
}
这里的逻辑非常简单,例如找到所有选定小部件的 min()
x
和 y
值,但我不确定我应该如何处理减速器组成,因为 currentSelectedWidgets
reducer无法访问小部件
州树的一部分。我考虑过......
- 将reducer合并为一个reducer:这似乎不是一个很好的可扩展解决方案。
- 通过动作传递当前小部件列表:这看起来特别讨厌。
- 找到更好的方法来建模状态树:我有一个想法但是任何替代版本似乎都有其他缺点。
- 构建一个自定义
combineReducers()
,可以将小部件列表提供给我的currentSelectedWidgets()
reducer作为一个额外的参数:我认为这可能是我最好的选择。
这是重新选择:
Cr选择一个选择器来访问你的状态并返回派生的数据。
例如:
从'reselect'导入{createSelector}
const widgetsSelector = state => state.widgets;
const selectedWidgetsSelector = state => state.selection.widgets;
函数minCoordinateSelector(小部件,已选中){
const x_list = selected.map((widget)=> {
return widgets [widget] .x;
});
const y_list = selected.map((widget)=> {
return widgets [widget] .y;
});
返回{
x:Math.min(... x_list),
y:Math.min(... y_list)
};
}
const coordinateSelector = createSelector(
widgetsSelector,
selectedWidgetsSelector,
(widgets,selected)=> {
return minCoordinateSelector(小部件,已选中);
}
);
coordinateSelector
现在可以访问你的分钟 x
和 y
属性。只有当 widgetsSelector
或 selectedWidgetsSelector
状态发生变化时,才会更新上述选择器,这使得这种访问方式非常有效您所追求的属性,避免在州树中重复。
I am working on a React/Redux application that allows for "widgets" to be added to a page and manipulated in 2D space. It is a requirement that multiple widgets can be selected and manipulated at once. A simplified version of my current state tree looks something like the following...
{
widgets: {
widget_1: { x: 100, y: 200 },
widget_2: { x: 300, y: 400 },
widget_3: { x: 500, y: 600 }
},
selection: {
widgets: [ "widget_1", "widget_3" ]
}
}
I currently have this tree managed by 2 reducers one managing widgets
state and the other managing selection
state. The selection state reducer can be simplified as (note: I am using Immutable.js too)...
currentlySelectedWidgets(state = EMPTY_SELECTION, action) {
switch (action.type) {
case SET_SELECTION:
return state.set('widgets' , List(action.ids));
default:
return state
}
}
This all seems to work quite well but I now have a requirement that I am struggling to fit into this model...
For UI purposes I need to have the current selection have an x/y property which is reflective of the upper left corner of the currently selected widgets x/y co-ordinates. An example state might look like...
{
widgets: {
widget_1: { x: 100, y: 200 },
widget_2: { x: 300, y: 400 },
widget_3: { x: 500, y: 600 }
},
selection: {
x: 100,
y: 200,
widgets: [ "widget_1", "widget_3" ]
}
}
The logic here is fairly simple, e.g. find the min()
of all the selected widgets x
and y
values but I am unsure of how I should handle this in terms of reducer composition as the currentlySelectedWidgets
reducer has no access to the widgets
part of the state tree. I have considered...
- merging the reducers into one reducer: this doesn't seem like a great scalable solution.
- passing the current list of widgets around with the action: this seems particularly nasty.
- finding a better way to model the state tree: I've had a think but any alternative versions seem to have other drawbacks.
- Building a custom
combineReducers()
that can feed the widgets list into mycurrentlySelectedWidgets()
reducer as an additional argument: I think this might be my best bet.
I am keen to hear any other suggestions on how others are managing similar situations, it seems like managing a "current selection" must be a common state problem that others must have had to solve.
This is a good use case for Reselect:
Create a selector to access your state and return derived data.
As an example:
import { createSelector } from 'reselect'
const widgetsSelector = state => state.widgets;
const selectedWidgetsSelector = state => state.selection.widgets;
function minCoordinateSelector(widgets, selected) {
const x_list = selected.map((widget) => {
return widgets[widget].x;
});
const y_list = selected.map((widget) => {
return widgets[widget].y;
});
return {
x: Math.min(...x_list),
y: Math.min(...y_list)
};
}
const coordinateSelector = createSelector(
widgetsSelector,
selectedWidgetsSelector,
(widgets, selected) => {
return minCoordinateSelector(widgets, selected);
}
);
The coordinateSelector
now provides access to your min x
and y
properties. The above selector will only be updated when either the widgetsSelector
or selectedWidgetsSelector
state changes, making this a very performant way to access the properties you are after and avoids duplication in your state tree.
这篇关于如何用依赖状态组成redux减速器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!