首页 > 解决方案 > Can I use condition in my action reducer?

问题描述

Basically, in our case, we need to either get an alerts list that shows the first few items (mounting it first time in the DOM) or show the initial list + the next list (clicking a load more button).

Hence we needed to do this condition in our GET_ALERTS action:

case "GET_ALERTS":
    if (action.initialList) {
        newState.list = [...newState.list, action.res.data.list];
    } else {
        newState.list = newState.list.concat(
            action.res.data.list
        );
    }

And when we call the action reducer in our Alerts component, we need to indicate whether initialList is true or false.

E.g.

componentDidMount() {
    this.props.getAlerts(pageNum, true);
}

markAllAsRead() {
   // other code calling api to mark all as read
   this.props.getAlerts(pageNum, false);
}

readMore() {
  // other code that increases pageNum state counter
  this.props.getAlerts(pageNum, true);
}

Anyway in such a case, is it fine to use conditional statement in the reducer?

标签: javascriptreactjsredux

解决方案


我反对这个想法。reducer 的职责只有一个:根据 action 更新 Redux 状态。

以下是解决此问题的三种方法:

简单的方法 - 将 Redux 状态下的列表初始化为空列表

如果您将列表设置state为空列表([]),那么它会简单得多。您基本上可以将减速器更改为:

case "GET_ALERTS":
    return {...state, list: [...state.list, action.res.data.list]

这将确保即使您有初始列表或更多要添加到列表中的项目,它们也会被附加。无需添加任何逻辑 - 恕我直言,这很棒。

redux-thunk 和分离类型成两种不同的类型

创建两个动作:GET_INIT_ALERTSGET_MORE_ALERTS

switch(action.type) {
case "GET_INIT_ALERTS":
    return {...state, list: action.res.data.list }
case "GET_MORE_ALERTS":
    return {...state, list: [...state.list, ...action.res.data.list]}
case "CHECK_READ_ALERTS":
    return {...state, read: [...state.read, ...action.res.data.list]}
}

在组件中,我将拥有:

componentDidMount() {
    this.props.getInitAlerts();
}

markAllAsRead() {
   // other code calling api to mark all as read
   this.props.getAlerts(pageNum, false);
}

readMore() {
  // other code that increases pageNum state counter
  this.props.getAlerts(pageNum);
}

在 redux-thunk 的帮助下进行警报操作:

export const getAlerts = (pageNum : number) => (dispatch) => {
    return apiAction(`/alerts/${pageNum}`, 'GET').then(res => dispatch({type: "GET_MORE_ALERTS", res});
}

export const getInitAlerts = () => (dispatch) => {
    return apiAction('/alerts/1', 'GET').then(res => dispatch({type: "GET_INIT_ALERTS", res});
}

我猜你在orpageNum之后更新。当然,您可以在 Redux 中保存该状态并将其映射回 props,并在调用操作时将其递增。readMorecomponentDidMountgetAlerts

编写自己的中间件

另一种方法是将临时/功能中间件写入concat列表中的新数据。

const concatLists = store => next => action => {
    let newAction = action
    if (action.type.includes("GET") && action.initialList) {
       newAction = {...action, concatList: action.res.data.list}
    } else if (action.type.includes("GET") {
       newAction = {...action, concatList: [...state[action.key].list, action.res.data.list]}
    }
    return next(newAction);
}

并将您的减速器更改为简单地推concatList送到状态:

case "GET_ALERTS":
    return {...state, list: action.concatList}

此外,您必须更改您的操作以包括key(在这种情况下,键将设置为alert(或您在 redux 中存储警报状态的键的名称)和 initialList 以确定是否连接。

顺便说一句,将这两个放在meta关键位置是一个好习惯。

{
  type: "GET_ALERT",
  meta: {
    initialList: true,
    key: "alert",
  },
  res: {...}
}

我希望这有帮助。


推荐阅读