如何组合具有依赖状态的 redux reducer
How to compose redux reducers with dependent state
我正在开发一个 React/Redux 应用程序,它允许 "widgets" 添加到页面并在 2D space 中进行操作。要求可以一次选择和操作多个小部件。我当前状态树的简化版本如下所示...
{
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" ]
}
}
我目前有这棵树由 2 个减速器管理,一个管理 widgets
状态,另一个管理 selection
状态。选择状态reducer可以简化为(注意:我也在使用Immutable.js)...
currentlySelectedWidgets(state = EMPTY_SELECTION, action) {
switch (action.type) {
case SET_SELECTION:
return state.set('widgets' , List(action.ids));
default:
return state
}
}
这一切似乎都运作良好,但我现在有一个要求,我正在努力适应这个模型......
出于 UI 目的,我需要让当前选择有一个 x/y 属性,它反映了当前所选小部件的左上角 x/y co -坐标。示例状态可能看起来像...
{
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" ]
}
}
这里的逻辑很简单,例如找到所有选定小部件的 min()
x
和 y
值,但我不确定我应该如何在 reducer 组合方面处理此问题,因为 currentlySelectedWidgets
reducer 无法访问到状态树的 widgets
部分。我考虑过...
- 将 reducer 合并为一个 reducer:这似乎不是一个很好的可扩展解决方案。
- 通过操作传递当前的小部件列表:这看起来特别讨厌。
- 寻找一种更好的方法来为状态树建模:我有一个想法,但任何替代版本似乎都有其他缺点。
- 构建自定义
combineReducers()
可以将小部件列表作为附加参数提供到我的 currentlySelectedWidgets()
reducer 中:我认为这可能是我最好的选择。
我很想听听关于其他人如何处理类似情况的任何其他建议,看来管理 "current selection" 一定是其他人必须解决的常见状态问题。
如果您使用 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
}
});
}
通过这种方式,您可以在选择缩减器中获得所需的所有信息,而无需将所有小部件对象的列表传递给您的操作。
这是 Reselect 的一个很好的用例:
创建一个选择器以访问您的状态和return 派生 数据。
举个例子:
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);
}
);
coordinateSelector
现在可以访问您的最小 x
和 y
属性。上面的选择器只会在 widgetsSelector
或 selectedWidgetsSelector
状态发生变化时更新,这使它成为访问您所追求的属性并避免在状态树中重复的一种非常高效的方式。
我正在使用这个:
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
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);
}
}
}
您可以尝试使用:
这允许您在代码中的任何位置获取状态,如下所示:
const localState1 = getState(reducerA.state1)
const localState2 = getState(reducerB.state2)
或者像这样将外部依赖状态分配给你的减速器:
reducerA.extState1 = reducerB.state1
然后像这样访问它:
getState(reduerA.extState1)
我正在开发一个 React/Redux 应用程序,它允许 "widgets" 添加到页面并在 2D space 中进行操作。要求可以一次选择和操作多个小部件。我当前状态树的简化版本如下所示...
{
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" ]
}
}
我目前有这棵树由 2 个减速器管理,一个管理 widgets
状态,另一个管理 selection
状态。选择状态reducer可以简化为(注意:我也在使用Immutable.js)...
currentlySelectedWidgets(state = EMPTY_SELECTION, action) {
switch (action.type) {
case SET_SELECTION:
return state.set('widgets' , List(action.ids));
default:
return state
}
}
这一切似乎都运作良好,但我现在有一个要求,我正在努力适应这个模型......
出于 UI 目的,我需要让当前选择有一个 x/y 属性,它反映了当前所选小部件的左上角 x/y co -坐标。示例状态可能看起来像...
{
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" ]
}
}
这里的逻辑很简单,例如找到所有选定小部件的 min()
x
和 y
值,但我不确定我应该如何在 reducer 组合方面处理此问题,因为 currentlySelectedWidgets
reducer 无法访问到状态树的 widgets
部分。我考虑过...
- 将 reducer 合并为一个 reducer:这似乎不是一个很好的可扩展解决方案。
- 通过操作传递当前的小部件列表:这看起来特别讨厌。
- 寻找一种更好的方法来为状态树建模:我有一个想法,但任何替代版本似乎都有其他缺点。
- 构建自定义
combineReducers()
可以将小部件列表作为附加参数提供到我的currentlySelectedWidgets()
reducer 中:我认为这可能是我最好的选择。
我很想听听关于其他人如何处理类似情况的任何其他建议,看来管理 "current selection" 一定是其他人必须解决的常见状态问题。
如果您使用 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
}
});
}
通过这种方式,您可以在选择缩减器中获得所需的所有信息,而无需将所有小部件对象的列表传递给您的操作。
这是 Reselect 的一个很好的用例:
创建一个选择器以访问您的状态和return 派生 数据。
举个例子:
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);
}
);
coordinateSelector
现在可以访问您的最小 x
和 y
属性。上面的选择器只会在 widgetsSelector
或 selectedWidgetsSelector
状态发生变化时更新,这使它成为访问您所追求的属性并避免在状态树中重复的一种非常高效的方式。
我正在使用这个:
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
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);
}
}
}
您可以尝试使用:
这允许您在代码中的任何位置获取状态,如下所示:
const localState1 = getState(reducerA.state1)
const localState2 = getState(reducerB.state2)
或者像这样将外部依赖状态分配给你的减速器:
reducerA.extState1 = reducerB.state1
然后像这样访问它:
getState(reduerA.extState1)