How to dynamically dispatch and wait for promises

Viewed 97

How do i dispatch a dynamically determined amount of times through redux?

I have users who are able to create lists of items and create as many as they like. When they navigate to an item page they can choose which lists to add it to.

This means that i may have to dispatch adding an item to one list OR MORE.

I want to dispatch the action to receive my updated lists only if all dispatches to 'add an item' to a list return a promise.

If i iterate through an array and pass in an argument to dispatch with is there a way to wait on a promise before continuing to the next step/array-index?

eg i'd need to call something like this several times but how many times will be determined by user and should only

export const addToList = (user_id, list_id, stock_ticker) => dispatch => {
    
    return StockApiutil.addToList(user_id, list_id, stock_ticker)
        .then(lists => dispatch(receiveLists(lists)))
                       
};

export const addToAllLists = (user_id, list_ids, stock_ticker) => dispatch => {
   dispatch(startListLoading());
   list_ids.map( list_id => 
     addToList(user_id, list_id, stock_ticker)
  )
  .then(dispatch(stopListLoading()))
}

This doesn't work because it doesn't return a promise and if i use a promise.all i won't create an array corresponding to final state for the lists.

1 Answers

You can do the following:

export const addToList = (
  user_id,
  list_id,
  stock_ticker
) => (dispatch) => {
  //you are returning a promise here, that is good
  return StockApiutil.addToList(
    user_id,
    list_id,
    stock_ticker
  ).then((lists) => dispatch(receiveLists(lists)));
};

export const addToAllLists = (
  user_id,
  list_ids,
  stock_ticker
) => (dispatch) => {
  dispatch(startListLoading());
  //return a single promise using Promise.all
  return Promise.all(
    list_ids.map((list_id) =>
      //also add (dispatch) so you actually call the thunk
      addToList(user_id, list_id, stock_ticker)(dispatch)
    )
  ).then(()=>dispatch(stopListLoading()));
};

There was a syntax error in the last line, should have been .then(()=>dispatch(stopListLoading())); looking at your parameter names I can see you are not used to write JS code as it's easy to spot if you run it, below is a working example:

const { Provider, useDispatch } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;

//actions
const later = (...args) =>
  new Promise((r) => setTimeout(() => r(args), 100));
const StockApiutil = {
  addToList: (a, b, c) => later(a, b, c),
};
const receiveLists = (list) => ({
  type: 'recieveList',
  payload: list,
});
const startListLoading = (payload) => ({
  type: 'startListLoading',
  payload,
});
const stopListLoading = (payload) => ({
  type: 'stopListLoading',
  payload,
});
const addToList = (user_id, list_id, stock_ticker) => (
  dispatch
) => {
  return StockApiutil.addToList(
    user_id,
    list_id,
    stock_ticker
  ).then((lists) => dispatch(receiveLists(lists)));
};

const addToAllLists = (user_id, list_ids, stock_ticker) => (
  dispatch
) => {
  dispatch(startListLoading());
  //return a single promise using Promise.all
  return Promise.all(
    list_ids.map((list_id) =>
      //also add (dispatch) so you actually call the thunk
      addToList(user_id, list_id, stock_ticker)(dispatch)
    )
  ).then(() => dispatch(stopListLoading()));
};
const reducer = (state, { type, payload }) => {
  console.log('in reducer:', type, payload);
  return state;
};
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  {},
  composeEnhancers(
    applyMiddleware(
      ({ dispatch, getState }) => (next) => (action) =>
        //simple thunk implementation
        typeof action === 'function'
          ? action(dispatch, getState)
          : next(action)
    )
  )
);
const App = () => {
  const dispatch = useDispatch();
  React.useEffect(
    () =>
      dispatch(
        addToAllLists(
          'user id',
          [1, 2, 3, 4, 5],
          'stock ticker'
        )
      ),
    [dispatch]
  );
  return 'check the console';
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>

Related