reggie3
reggie3

Reputation: 1058

Calling State Altering Reducer Functions Sequentially in Redux

I'm learning Redux, and am running into a problem related to ensuring a branch (Branch A) of my state tree has completed its update before I update another branch (Branch B). Branch B is dependent on the ID that is assigned to the newly updated item in Branch A, so I want Branch B's update function to dispatch after the dispatched function that updates Branch A is complete. I have multiple reducers that correspond to the various branches, and I am using combineReducers to combine them.

I use the following to dispatch the action updating Branch A from a component:

/* update Branch A */
this.props.dispatch(actions.addCriterion(this.state));

Sequentially dispatching the action that updates Branch B on the next line of code results in Branch B not seeing the updated state information caused by Branch A's dispatch.

/* update Branch B */
this.props.dispatch(actions.completeScoreGrid(this.props.alternatives, this.props.criteria));

However, everything works as desired if I use a setTimeout and wait a bit after A is dispatched before calling B. In this case the state update is complete and B see's everything it should

/* update Branch B after a 1 second pause */    
    var that = this;
    setTimeout(function() {
        that.props.dispatch(actions.completeScoreGrid(that.props.alternatives, 
    that.props.criteria));
    }, 1000);

However, I'm sure using a set timeout is not the proper way of doing this. I've looked as redux-thunk, redux-promise, redux-promise-middleware, and redux-saga and I'm not sure which of these is the proper tool to use for this problem or whether any of them are the proper tool. What is the correct way to solve this kind of problem?

Upvotes: 1

Views: 619

Answers (2)

reggie3
reggie3

Reputation: 1058

I ended up using thunk-redux to solve the problem and marking Brandon Lewis's post above as the answer.

Also, there is another technique posted by Reddit user cyex in my Reddit post asking this same question. I tested it, and it also did what I wanted to accomplish. I've linked to the post below as well as pasted his solution here.

Link to Reddit post on this question

Can you post what your reducers look like? It doesn't sound right that the new state wouldn't be reflected in the second call. But also when you say that Branch B is dependent on the ID that is assigned to the new item in Branch A... are you generating this ID in your reducer or in your action creator? e.g. What if your code looked like this instead?

/* update Branch A */
var newCriterion = actions.addCriterion(this.state); // ID is assigned here, not in reducer
this.props.dispatch(newCriterion);

/* update Branch B */
var scoreGrid = actions.completeScoreGrid(this.props.alternatives, this.props.criteria, newCriterion.id);
this.props.dispatch(scoreGrid);
So the B reducer doesn't need to look at anything on another branch.

Upvotes: 0

Brandon
Brandon

Reputation: 891

The problem here is that the criteria prop inside the component from which you dispatched these actions hasn't had a chance to update. However, the value of criteria inside the state atom itself has already updated. Allow me to explain this further.

Let's call this line of code Line 1: this.props.dispatch(actions.addCriterion(this.state));

And let's call this line of code Line 2: this.props.dispatch(actions.completeScoreGrid(this.props.alternatives, this.props.criteria));

After Line 1 has completed, the criteria in the redux state atom includes the new criterion. However, the criteria prop in Line 2 has not been refreshed yet, since React hasn't yet updated the component in response to the redux state change.

So how do you fix this? There are a few ways, but here's my best suggestion. Instead of passing in this.props.criteria to your completeScoreGrid action creator, you can use redux-thunk to enable async actions, and get the criteria from the redux state atom inside the async action creator. Here's how that async action creator might look, assuming the criteria are stored at state.criteria.

function completeScoreGrid(alternatives) {
  return (dispatch, getState) => {
    const state = getState();
    const criteria = state.criteria;
    dispatch({
      type: 'COMPLETE_SCORE_GRID',
      payload: {
        criteria,
        alternatives
      }
    });
  }
}

You can then update Branch B in response to this action. Let me know if you have any questions.

Upvotes: 2

Related Questions