erikras / react-redux-universal-hot-example

A starter boilerplate for a universal webapp using express, react, redux, webpack, and react-transform
MIT License
12.01k stars 2.5k forks source link

multiple promises in action creator #1227

Open paulinda opened 8 years ago

paulinda commented 8 years ago

I have the following action creator: export function loadData() { return { types: [LOAD, LOAD_SUCCESS, LOAD_FAIL], promise: (client) => client.get(LOAD_DATA_URL) }; } What I need to do is make another api call with the data is returned from this action. I tried using a 'then' but then the store doesn't get updated. I also tried to call another action from the component to get the additional data. This is successful as the data shows up in the server console but in the browser, the action fails in a loop. What is the proper way to do this? Should an action launch another action? Any samples would be greatly appreciated.

jaraquistain commented 8 years ago

The way I've handled this situation is to have the two actions as you normally would and chain them when calling the function. Here's an example where I have an action that removes something, then changes routes via an action dispatch, then shows an alert success banner via a 3rd action:

dispatchRemove(data, url)
  .then(() => routerActions.push(destination))
  .then(() => addAlert("Remove Node: Success!", "success"));

This is inside the click handler for the remove button. All the actions were injected as props via mapDispatchToProps

paulinda commented 8 years ago

Thanks @jaraquistain

Here is the final working code: @asyncConnect([{ promise: ({store: {dispatch, getState}}) => { const promises = [];

if (!isDataLoaded(getState())) {
  promises.push(dispatch(loadData())
    .then(() => dispatch(loadValues(getState()))
    .then(() => dispatch(loadeBayItems(getState())))
    ));
}
if (!isImageLoaded(getState())) {
  promises.push(dispatch(loadImage()));
}
return Promise.all(promises);

} }])

oyeanuj commented 7 years ago

@jaraquistain @paulinda Were you able to do the same from within the action-creators?

So, something like this?

//in loadData function for example:
return {
  id: 1,    
  types: [
      DATA_REQUESTED,
      DATA_REQUEST_SUCCESS,
      DATA_REQUEST_FAIL
    ],
  promise: (client) =>
    client.get(requestQuery())
    .then((response) => normalize(response, arrayOf(contentSchema)))
    .then(() => loadSomethingElse())
};      

This doesn't work for me but I was wondering if you've had any luck with that?

paulinda commented 7 years ago

@oyeanuj I wasn't able to get it to work from within an action creator. I did get it working from within the component using asyncConnect. The code is above.

ryanlward commented 7 years ago

Here is how I do this in my reducer

export function load() {
  return {
    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],
    promise: (client) => client.get('/collection/load')
  };
}

export function saveItem(item) {
  return {
    types: [SAVE_ITEM, SAVE_ITEM_SUCCESS, SAVE_ITEM_FAIL],
    promise: (client) => client.post('/collection/save', {
      data: item
    })
  };
}

export function saveItemAndReloadCollection(item) {
  return (dispatch) => {
    return dispatch(saveItem(item))
    .then(() => {
      return dispatch(load());
    });
  };
}

I think you might be able to accomplish what you're trying to do by doing something like:

export function saveItemAndReloadCollection(item) {
  return (dispatch, getState) => {
    return dispatch(loadItems())
    .then(() => {
      const items = getState().items;
      //do whatever
    });
  };
}