最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Can I use condition in my action reducer? - Stack Overflow

programmeradmin3浏览0评论

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 ponent, we need to indicate whether initialList is true or false.

E.g.

ponentDidMount() {
    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?

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 ponent, we need to indicate whether initialList is true or false.

E.g.

ponentDidMount() {
    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?

Share Improve this question asked Oct 30, 2018 at 4:39 catandmousecatandmouse 11.8k24 gold badges95 silver badges158 bronze badges 2
  • You could consider using getState() in your action creator to conditionally create the appropriate payload before dispatching to the reducer to avoid conditional logic in your reducer. – Alexander Staroselsky Commented Oct 30, 2018 at 4:49
  • This might help stackoverflow./questions/45874393/conditions-in-reducers – Praveen Commented Oct 30, 2018 at 4:52
Add a ment  | 

2 Answers 2

Reset to default 5

I am against this idea. The reducer has a single responsibility: update Redux state according to the action.

Here are three ways to slove this:

easy way - initialize your list in Redux state to empty list

if you set the list in state to empty list ([]) then it's much simpler. You can basically just change your reducer to this:

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

This will make sure that even if you have get initial list or more items to add to the list, they will be appended. No need to add any logic - which is awesome IMHO.

redux-thunk and separating type into two different types

create two actions: GET_INIT_ALERTS and GET_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]}
}

In the ponent I will have:

ponentDidMount() {
    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);
}

In alerts action with the help of 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});
}

I guess you update pageNum after readMore or ponentDidMount. Of course you can save that state in Redux and map it back to props and just increment it when calling the getAlerts action.

write your own middleware

Another way to do this is to write an ad-hoc/feature middleware to concat new data to a list.

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

And change your reducer to simply push concatList to the state:

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

In addition, you will have to change your action to include key (in this case the key will be set to alert (or the name of the key where you store the alert state in redux) and initialList to determine whether to concat or not.

BTW, it's a good practice to put these two under the meta key.

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

I hope this helps.

I would suggest you to have following set of actions:

  • ALERTS/INIT - loads initial list
  • ALERTS/LOAD_MORE - loads next page and then increments pageNo, so next call will know how many pages are loaded
  • ALERTS/MARK_ALL_AS_READ - does server call and reinitializes list

The store structure

{
    list: [],
    currentPage: 0
}

And ponent code should not track pageNum

ponentDidMount() {
    this.props.initAlerts();
}

markAllAsRead() {
   this.props.markAllAsRead();
}

readMore() {
   this.props.loadMore();
}
发布评论

评论列表(0)

  1. 暂无评论