Where to dispatch multiple actions in redux?

JavascriptRedux

Javascript Problem Overview


I am using redux with connect and redux-thunk middleware and containers.

Currently when an user perform an action, example one click on a button, I need to dispatch that action (sync) which will dispatch other few actions (asynch).

I am aware dispatching actions from within the reducer is an anti pattern.

I would like to know what is a suitable place for this code.

Currently I am not sure if it should stay in:

  • The action creator.
  • In the container using store.subscribe.

Javascript Solutions


Solution 1 - Javascript

The recommended way as per the documentation is in the action creator, like so:

function actionCreator(payload) {
    return dispatch => {
        dispatch(action1(payload))
        dispatch(action2(payload))
    }
}

Then you would probably want to attach the action creators as prop and pass it down to the container using mapDispatchToProps like in the example mentioned here. So it would look something like so:

const mapDispatchToProps = dispatch => ({
   action1: some_payload => dispatch(action1(some_payload))
   action2: some_payload => dispatch(action2(some_payload))
})

// your component
export default connect(mapStateToProps, mapDispatchToProps)(YourApp)

Solution 2 - Javascript

As other pointed out The action creator is the right place for dispatching multiple actions.

Below an example of how action1 could dispatch other actions in your action creator.

const action1 = id => {
  return dispatch => {
    dispatch(action2(id))
    dispatch(action3(id))
  }
}

Solution 3 - Javascript

The action creator is the correct location for dispatching multiple actions. Although code like the following would work:

function actionCreator(payload) {
    return dispatch => {
        dispatch(action1(payload))
        dispatch(action2(payload))
    }
}

I would highly recommend redux-thunk based action creators to always return a resolved Promise, so that such action creators can be part of another async call. So, the simplest update to the above would be:

function actionCreator(payload) {
    return dispatch => {
        dispatch(action1(payload));
        dispatch(action2(payload));
        return Promise.resolve();
    }
}

It would then be possible to dispatch to the above with: actionCreator(payload).then(doAnotherAction(anotherPayload)) or the following, if we need to maintain order of calls: actionCreator(payload).then(() => doAnotherAction(anotherPayload))

If you wish to 'future-proof' your action creator, so that it could handle calling both async and sync action creators, you could write it as:

function actionCreator(payload) {
    return dispatch =>
        Promise.resolve(dispatch(action1(payload))).then(
        () => dispatch(action2(payload)));
}

And, if you like ES6 arrow notation the above can be defined as:

const actionCreator = payload => dispatch =>
        Promise.resolve(dispatch(action1(payload))).then(
        () => dispatch(action2(payload)));

Solution 4 - Javascript

While solution by @GibboK did not work for me:

const mapDispatchToProps = (dispatch) => ({
  action2: id => dispatch(Actions.action2(id)),
  action3: id => dispatch(Actions.action3(id)),
  action1: (dateId, attrId) => {
    return dispatch => {
      dispatch(Actions.action2(dateId));
      dispatch(Actions.action3(attrId));
    }
  }
});

I eventually went with redux-batched-actions. Worked like charm:

const mapDispatchToProps = (dispatch) => ({
  action2: id => dispatch(Actions.action2(id)),
  action3: id => dispatch(Actions.action3(id)),
  action1: (dateId, attrId) =>
    dispatch(batchActions([
      Actions.action2(dateId),
      Actions.action3(attrId)
    ]))
});

Solution 5 - Javascript

If you have a Promise Middleware, you can use this syntax so you're able to use .then() on your dispatch(topLevelAction()):

export const topLevelAction = () => dispatch => {
    return Promise.all([dispatch(action1()), dispatch(action2()), dispatch(action3())])
}

Solution 6 - Javascript

"had similar issue. had to create a function 
that accepts object with  the actions you want to 
dispatch to the store and individual params for 
respective action"

dispatchMultiple({params: {
    params1: "<arg for first action>" ,
    params2: "<arg for second action>",
                        },

})
const dispatchMultiple = (obj) => {
    dispatch(obj.actions.action1(obj.params.params1));
    dispatch(obj.actions.action2(obj.params.params2));
  }; 

Solution 7 - Javascript

For guys in 2020... The actions are Supposed to be made in the action Creater. For those who would like to dispatch an action and fetch/post some data from the API can use this Idea.

lets assume we have an actions.js file and we want to dispatch a loading action before fetch data.

function requestPosts() {
    return {
      type: "loading"
    }
  }

This is the fetching action function

function fetchPosts() {
 return dispatch => {
    // dispatch the loading
    dispatch(requestPosts());
    // fetch data from api
  return fetch("https://www.yoururl.com/api")
   .then(response => response.json())
   .then(json => dispatch({
       type: "fetching successful",
       payload: json
    }));
  }
}

Solution 8 - Javascript

I don't know the exact use case but since redux uses asynchronous logic, any solution that runs the second dispatch in the next tick of the event loop should work.

store.dispatch({ type: 'ADD_TODO', text: 'Buy milk.' });

setTimeout(() => {
  store.dispatch({ type: 'ADD_TODO', text: 'Take out garbage.' });
}, 0);

Promise.resolve(() => {
  store.dispatch({ type: 'ADD_TODO', text: 'Water plants.' });
});

If the second dispatch depends on the actions of first dispatch, you can get the state from the store, check if it satisfies the condition then dispatch the second action. It is best to keep the action's logic clean and granular.

So, to answer the question, the right place to dispatch multiple actions is inside the click handler where the first action originates.

Solution 9 - Javascript

This is what worked for me for synchronous multiple actions:

// utils file
const multipleActions = (dispatch) => {
  dispatch(action1())
  dispatch(action2())
  dispatch(action3())
}

const mapDispatchToProps = (dispatch) => {
   onClickReturn: () => {
      multipleActions(dispatch)
   }
};

Solution 10 - Javascript

The easiest way is to use a specialized middleware redux-soldier:

import { createStore, applyMiddleware } from 'redux'
import { reduxSoldierMiddleware } from 'redux-soldier'

const store = createStore(rootReducer, applyMiddleware(reduxSoldierMiddleware))
store.dispatch([
  {type: 'INCREMENT'}, // traditional action
  addTodo('Start using redux-soldier'), // action creator
  fetchUser(), // thunk action
])

redux-soldier is also a full replacement for redux-thunk

For more info check the documentation redux-soldier.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRadexView Question on Stackoverflow
Solution 1 - JavascriptMμ.View Answer on Stackoverflow
Solution 2 - JavascriptGibboKView Answer on Stackoverflow
Solution 3 - JavascriptShirazView Answer on Stackoverflow
Solution 4 - JavascriptBugs BunnyView Answer on Stackoverflow
Solution 5 - JavascriptRubbelDieKatzView Answer on Stackoverflow
Solution 6 - JavascriptbonsuDanielView Answer on Stackoverflow
Solution 7 - JavascriptBryan AustinView Answer on Stackoverflow
Solution 8 - JavascriptsnnsnnView Answer on Stackoverflow
Solution 9 - JavascriptAakashView Answer on Stackoverflow
Solution 10 - JavascriptGherciu GheorgheView Answer on Stackoverflow