如何组成具有依赖状态的减速器



我正在开发一个React/Redux应用程序,该应用程序允许将"小部件"添加到页面中并在2D空间中进行操作。要求可以同时选择和操作多个小部件。我当前状态树的简化版本如下。。。

{
  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" ]
  }
}

我目前有两个管理widgets状态和selection状态的减速器来管理这个树。选择状态缩减器可以简化为(注意:我也在使用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坐标的左上角。一个示例状态可能看起来像。。。

{
  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" ]
  }
}

这里的逻辑相当简单,例如,找到所有选定窗口小部件xy值的min(),但我不确定应该如何处理reducer组合,因为currentlySelectedWidgets reducer无法访问状态树的widgets部分。我考虑过。。。

  • 将减速器合并为一个减速器:这似乎不是一个很好的可扩展解决方案
  • 通过操作传递当前的小部件列表:这似乎特别令人讨厌
  • 找到一种更好的方法来建模状态树:我有一个想法,但任何替代版本似乎都有其他缺点
  • 构建一个自定义的combineReducers(),它可以将小部件列表馈送到我的currentlySelectedWidgets() reducer中,作为一个额外的参数:我认为这可能是我的最佳选择

我很想听听其他人如何处理类似情况的任何其他建议,似乎管理"当前选择"一定是其他人必须解决的常见状态问题。

这是Reselect:的一个很好的用例

创建一个选择器来访问您的状态并返回派生的数据。

例如:

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现在可以访问您的最小xy属性。只有当widgetsSelectorselectedWidgetsSelector状态发生变化时,才会更新上述选择器,这是一种非常高效的方式来访问您想要的属性,并避免状态树中的重复。

如果使用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
            }
        });
}

这样,您就可以在选择缩减器中获得所需的所有信息,而无需将所有小部件对象的列表传递给您的操作。

我使用的是:

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;
  }
}

SometerReducer.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);
         }
      }
}

您可以尝试使用:

redux命名减速器

这允许你在代码中的任何地方获得状态,比如:

const localState1 = getState(reducerA.state1)
const localState2 = getState(reducerB.state2)

或者将外部依赖状态分配给您的减速器,如下所示:

reducerA.extState1=reducerA.state1

然后像这样访问:

getState(reduerA.extState1)

最新更新