[英]How to compose redux reducers with dependent state
I am working on a React/Redux application that allows for "widgets" to be added to a page and manipulated in 2D space. 我正在开发一个React / Redux应用程序,它允许将“小部件”添加到页面并在2D空间中进行操作。 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. 我目前有这个树由2个reducer管理,一个管理widgets
状态,另一个管理selection
状态。 The selection state reducer can be simplified as (note: I am using Immutable.js too)... 选择状态reducer可以简化为(注意:我也使用Immutable.js)...
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. 出于UI目的,我需要让当前选择具有x / y属性,该属性反映当前所选小部件x / y坐标的左上角。 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, eg 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. 这里的逻辑非常简单,例如找到所有选定小部件x
和y
值的min()
,但我不确定如何根据reducer组合处理这个,因为currentlySelectedWidgets
reducer无法访问widgets
部分。州树。 I have considered... 我考虑过......
combineReducers()
that can feed the widgets list into my currentlySelectedWidgets()
reducer as an additional argument: I think this might be my best bet. 构建一个定制的combineReducers()
可以养活小部件列表进入我的currentlySelectedWidgets()
减速机作为额外的参数:我想这可能是我最好的选择。 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 : 这是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. 现在, coordinateSelector
提供了对min x
和y
属性的访问。 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. 只有当widgetsSelector
或selectedWidgetsSelector
状态发生变化时,才会更新上述选择器,这使得这是一种非常widgetsSelector
方式来访问您所追求的属性,并避免在状态树中出现重复。
If you use the thunk middleware ( https://github.com/gaearon/redux-thunk ) you can make the SET_SELECTION
action a thunk, which will allow it to read entire state before making the dispatch that will be received by your reducer. 如果您使用thunk中间件( https://github.com/gaearon/redux-thunk ),您可以将SET_SELECTION
操作设置为thunk,这将允许它在进行reducer接收的调度之前读取整个状态。
// action creator
function setSelection(selectedWidgetId) {
return (dispatch, getState) => {
const {widgets} = this.getState();
const coordinates = getSelectionCoordinates(widgets, selectedWidgetIds);
dispatch({
type: SET_SELECTION,
payload: {
widgets: selectedWidgets,
x: coordinates.x,
y: coordinates.y
}
});
}
This way you get all the information you need in your selection reducer without having to pass along the list of all widget-objects to your action. 这样,您可以在选择缩减器中获得所需的所有信息,而无需将所有窗口小部件对象的列表传递给您的操作。
I am using this: 我用这个:
CommonReducer.js CommonReducer.js
export default function commonReducer(state = initialState.doesNotMatter, payload) {
switch (payload.type) {
case "JOINED_TYPE": {
let newState = Object.assign({}, state);
return newState;
}
default:
return state;
}
}
SomeOtherReducer.js SomeOtherReducer.js
import commonReducer from './commonReducer';
export default function someReducer(state = initialState.somePart, payload) {
switch (payload.type) {
case "CUSTOM_TYPE": {
let newState = Object.assign({}, state);
return newState;
}
default:{
return commonReducer(state, payload);
}
}
}
You can try to use: 您可以尝试使用:
Which allows you to get state anywhere in your code like so: 这允许您在代码中的任何位置获取状态,如下所示:
const localState1 = getState(reducerA.state1)
const localState2 = getState(reducerB.state2)
Or to assign external dependent state to your reducers like so: 或者将外部依赖状态分配给Reducer,如下所示:
reducerA.extState1 = reducerB.state1 reducerA.extState1 = reducerB.state1
Then access it like so: 然后像这样访问它:
getState(reduerA.extState1) 的getState(reduerA.extState1)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.