减速行为

时间:2017-08-25 21:46:45

标签: reactjs redux react-redux

据我所知,当一个动作被调用时,所有减速器都会响应。如果reducer的switch case语句中存在操作,则执行该操作。如果它没有,则case: default执行保留现有状态。

当reducer中存在该操作但其尝试更新的特定属性不存在时,它似乎表现良好,因为没有更新。

例如,我有一个动作创建器,用于设置我的模态的visible属性。每个模态都有自己的Id。我的代码如下所示:

export default (state = initialState, action) => {
   case types.SET_MODAL_IS_VISIBLE:
      return Object.assign({}, state,
         { modal22: action.value }
      )}

我在多个reducer中有SET_MODAL_IS_VISIBLE,但如果在特定的reducer中没有定义modal22,则没有任何反应,也没有错误。

现在,我有一个抛出错误的场景。我有一个我建立的通用日期选择器组件,可以用作单个独立的日期选择器,或者它可以"链接到"另一个。如果我需要用户给我两个日期,第二种情况很有用,例如开始和结束日期。

我还建立了一个功能,如果日期选择器与另一个日期选择器耦合,当用户在第一个日期选择器中设置日期时,我会在第二个日期选择器中禁用该日期之前的所有日期,因为我没有&#39 ; t希望用户无意中选择开始日期之前的结束日期。

我将日期选择器定义如下:

const initialState = {
    datePickers: {
        "startDatePicker": {
            activeDate: "8/25/2017",
            disabledBefore: "",
            linkedTo: "endDatePicker"
         },
         "endDatePicker": {
            activeDate: "",
            disabledBefore: "8/25/2017" // This date is set when the user sets the active date in startDatePicker
            linkedTo: ""
       }
    }
}

这种情况有点有趣,因为我的reducer中的一个属性的状态更改正在触发另一个属性的状态更改。这并不难做到,我有办法控制何时进行更新。

设置禁用日期的操作如下所示:

...
case types.SET_DISABLED_DATES:
    return Object.assign({}, state,
       datePickers: Object.assign({}, state.datePickers, {
          datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], {
             disabledBefore: action.value
          })
    })

请记住,即使日期选择器被用作独立选项,我也可以并且应该能够设置disabledBefore。所以,我需要在每个减速器中使用SET_DISABLED_DATES

我遇到的问题是,每当我调用SET_DISABLED_DATES时,我都会在reducer中出现错误,其中日期选择器用作单个/独立的,因为其对的日期选择器ID不是在reducer中定义。

例如,在projectsReducer中,我可以使用日期选择器作为一对的一部分,因此startDatePickerendDatePicker都已定义,一切正常。

但我可能在tasksReducer中使用单个实例日期选择器,它也响应SET_DISABLED_DATES调用,但由于无法找到endDatePicker而失败。在这种情况下,tasksReducer正在响应我在disabledDates中设置endDatePicker projectsReducer属性的调用。

我已经发布了两个关于此的问题,我在这里看到的唯一真正的解决方案是我需要在我的减速机中找到一个如下情况:

...
case types.SET_DISABLED_DATES:
   if(typeof state.datePickers[action.datePickerId] !== "undefined") { // Making sure that what I'm trying to update exists in the reducer
      return Object.assign({}, state,
       datePickers: Object.assign({}, state.datePickers, {
          datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], {
             disabledBefore: action.value
          })
    })
   } else {
      return state;
   }

不可否认,这看起来有点像kludge但我在这里无法提出另一种解决方案。

同样,问题在于,只要所有缩减器都响应SET_DISABLED_DATES,就可以保证特定的日期选择器不会出现,而Object.assign()会抛出错误。

有什么建议吗?减速机的简单条件是这样的吗?它是一个kludge吗?

P.S。我尝试了这个代码,它工作正常并解决了问题。一方面,我觉得这有点反模式,但另一方面,在尝试更新之前,确保我想要在reducer中更新的属性似乎是个好主意。我很感激您对此的反馈。感谢。

2 个答案:

答案 0 :(得分:0)

在设置状态之前,您只是在reducer中进行基本验证。那很好。我不认为检查动作创建者中的商店以防止对不在商店中的对象进行调度操作是一个好习惯(无论如何你都会这样做!)。

我不明白的是,如何将日期选择器链接到商店中没有的另一个日期选择器?也许在组件didMountwillUnmount上发送创建和拆卸操作?

我不知道你的全部要求,但我认为我们可以让它变得更简单。我做这样的事情:

商店:

{
  datePickers: {
    id1: {
      value: '',
      minValue: '',
      maxValue: '',
    },
    id2: {
      value: '',
      minValue: '',
      maxValue: '',
    }
  }
}

现在,除非你制作一些总是会成对运行的耦合的datepicker组件,我相信最干净的方法是在父组件的mapDispactchToProps函数中设置链接的datepicker中的禁用值

这是您将id设置为组件的位置,并且您确切地知道应该在另一个组件之前禁用哪个组件。

类似的东西:

dispatch => ({
  setArrivalDate(value) {
    dispatch(datePickerActions.setValue(arrivalDateId, value);
    dispatch(datePickerActions.setMaxValue(depatureDateId, value);
  },
  setDepatureDate(value) {
    dispatch(datePickerActions.setValue(depatureDateId, value);
    dispatch(datePickerActions.setMinValue(arrivalDateId, value);
  }
})

这可能不够抽象,但很干净。

如果你有一个配对组件,你可以做同样的事情,但你仍然需要知道哪个日期在另一个之前。围绕它进行通用抽象是一件麻烦事。

答案 1 :(得分:0)

删除代码中的粗体部分

    ...
    case types.SET_DISABLED_DATES:
       if(typeof state.datePickers[action.datePickerId] !== "undefined") { // Making sure that what I'm trying to update exists in the reducer
          return Object.assign({}, state,
           datePickers: Object.assign({}, state.datePickers, {
              datePickers[action.datePickerId]: Object.assign({}, state.datePickers[action.datePickerId], {
                 disabledBefore: action.value
              })
        })
       } else {
          return state;
       }

此外,一点es6传播和辅助开关函数使这段代码更具可读性。

const newReducer = (state = defaultState, action) => switchcase({
  [types.SET_DISABLED_DATES]:
    state.datePickers[action.datePickerId] === undefined
      ? state
      : ({ ...state,
        datePickers: { ...state.datePickers,
          [action.datePickerId]: { ...state.datePickers[action.datePickerId],
            disabledBefore: action.value,
          },
        },
      }),
})(state)(action.type);

使用lodash/fp/set,代码变为

const reducerWithLodash = (state = defaultState, action) => 
switchcase({
  [types.SET_DISABLED_DATES]:
    state.datePickers[action.datePickerId] === undefined
      ? state
      : set({...state}, `datePickers.${action.datePickerId}.disabledBefore`, action.value)
})(state)(action.type)

我还没有测试过lodash版本,所以请带上一粒盐(Dan Abramov似乎approve