React-redux-universal-hot-example: multiple promises in action creator

Created on 14 Jul 2016  路  5Comments  路  Source: erikras/react-redux-universal-hot-example

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.

Most helpful comment

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

}
}])

All 5 comments

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

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

}
}])

@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?

@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.

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
    });
  };
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

bdsabian picture bdsabian  路  6Comments

yesmeck picture yesmeck  路  3Comments

sauravskumar picture sauravskumar  路  6Comments

krishnasaga picture krishnasaga  路  3Comments

eromoe picture eromoe  路  5Comments