redux-utilities / redux-promise

FSA-compliant promise middleware for Redux.
MIT License
2.67k stars 119 forks source link

Dispatching multiple actions in an action creator #14

Open ashaffer opened 9 years ago

ashaffer commented 9 years ago

What is the recommended way of doing something like this? Using redux-thunk, you receive dispatch in your thunk, but with this middleware, i'm not sure how i'd go about getting access to the dispatch method in my action creators. Sorry if i'm just missing something obvious.

tlrobinson commented 9 years ago

Also wondering about this. I need to make an API call to load data that depends on the result of another API call. What's the correct way to do this?

gabrielgiussi commented 9 years ago

Could we have something like

{
  type,
  payload,
  meta,
  whenDone: [type1, type2]
}

for addressing dependencies between actions? So when the action is done will fire two actions of types type1 and type2 with the payload.

in the middleware:

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    return isPromise(action.payload)
      ? action.payload.then(
          result => {
            for (type in action.whenDone) {
               dispatch({type: type, payload: result })
            }
         },
          error => dispatch({ ...action, payload: error, error: true })
        )
      : next(action);
  };
}
ashaffer commented 9 years ago

@gabrielgiussi I created a package to address this problem in a general way: redux-multi. Just return an array of actions from an action creator and it'll dispatch them all.

EDIT: I realize now this doesn't actually fully address the issue if you are using redux-promise. I am also working on an effect-middleware system that does address this issue over in redux-effects, though, if you'd like to check it out.

gabrielgiussi commented 9 years ago

That's great, but with redux-multi you can't achieve what @tlrobinson is asking for, don't you?

I've been wondering if an action that triggers another action fit in in the flux/redux pattern, so we start with a ADD_TODO that makes an async api call to the server, when it backs we could trigger ADD_TODO_SUCCESS or ADD_TODO with 'done'.

Can we fire a new action of another type (for example SEND_EMAIL for notify the added todo) when an action of type ADD_TODO is fired?

gsklee commented 9 years ago

@acdlite Anyway to do this kind of sequential triggering of events in reduc-promise like we can in redux-thunk? This is a must for any real-world app...

ashaffer commented 9 years ago

redux-effects let's you do exactly this.

gsklee commented 9 years ago

@ashaffer Could you give an example of using redux-effects with redux-actions and redux-promise to enable sequential action dispatching then?

ashaffer commented 9 years ago

@gsklee redux-effects would be used in place of redux-promise. An example using redux-effects-etch and redux-multi might look like this:

import {fetch} from 'redux-effects-fetch'
import {bind} from 'redux-effects'
import {createAction} from 'redux-actions'

const userIsLoading = createAction('USER_IS_LOADING')
const userDidLoad = createAction('USER_DID_LOAD')
const userDidError = createAction('USER_DID_ERROR')

function getCurrentUser () {
  return [
    bind(fetch('/user'), userDidLoad, userDidError),
    userIsLoading()
  ]
}

redux-effects dispatches the return value of all of your handlers. So when userDidLoad returns an action, it is dispatched back into redux. So, you could of course nest effects that culminate in actions later, like this:

function getUsersComments (userId) {
  return bind(
    fetch('/user/' + userId), 
    user => bind(fetch(user.commentsUrl), commentsDidLoad)
  )
}
Industrial commented 8 years ago

:+1: for multiple dispatches per action.

nicmesan commented 8 years ago

Guys, I managed to get it work by using the Promise.all() method. Check the example below:

deleteMultipleVideos(videoIdsArray){
    var responses = [];
    //iterate through every item and get it corresponding promise
    videoIdsArray.forEach((videoId)=>{
        var url = `${API_BASE_PATH}/assets/${videoId}`;
        var response = axios.delete(url, DEFAULT_REQUEST_CONFIG);
       //pushing all the promises into an array
        responses.push(response);
    });
    //waiting for all promises to be resolve before reaching the reducer
   //and then returning the array with the resolved promises
    return Promise.all(responses);
}

Hope it helps!

Industrial commented 8 years ago

I ended up writing https://github.com/Industrial/redux-streams since I'm not that into promises :-)

jpdjere commented 7 years ago

@nicmesan Belated thanks man. Your solution works. Just let your action return a Promise composed of an array of Promises with Promise.all(arrayOfPromises) and redux-promise will take care of solving all of them. Works like a charm!

antokara commented 6 years ago

Try this one:

const getRemoteDataActionFn = () => dispatch => (
  dispatch(createAction(
    'GET_REMOTE_DATA',
    async () => fetchAndDispatchExtraActionFn(dispatch),
  )())
);

It uses redux-thunk , redux-actions and redux-promise I believe this is what you originally wanted @ashaffer