reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.91k stars 15.27k forks source link

Sequential actions? #468

Closed StevenLangbroek closed 9 years ago

StevenLangbroek commented 9 years ago

I'm trying to figure out how to do sequential actions on startup. In my case this sort of looks like this in pseudo-code:

App.start()
  .then(getSetup)
  .then(fetchLocales)
  .then(fetchOffers)

We need getSetup action to finish before calling fetchLocales, as the setup store contains info we need for fetching the locales data... Any guidance here?

sergey-lapin commented 9 years ago

After store creation you can do store.dispatch(somePropmiseChain) you need redux-promise middleware as well I suppose to use promises as action creators.

johanneslumpe commented 9 years ago

@StevenLangbroek You could use the thunk middleware to create a bootstrap action creator, which contains your full promise chain and dispatches actions on the go.

StevenLangbroek commented 9 years ago

Sorry I should've been a bit more clear maybe. The chain looks more like this:

getSetup().
  then(setup => 
    // How do I ensure this has been reduced into the store...
    dispatch({
      type: FETCH_SETUP,
      payload: setup
    })
  )
  // ...before running this action? fetchTranslations needs
  // a subnode of the `setup` store...
  .then(fetchTranslations)
  .then(translations =>     
    dispatch({
      type: FETCH_TRANSLATIONS,
      payload: translations
    })
  )
  .then(fetchOffers)
  .then(offers => dispatch({
    type: FETCH_OFFERS,
    payload: offers
  }));
johanneslumpe commented 9 years ago

@StevenLangbroek how about something like this:

import { getSetup, fetchTranslations, fetchOffers} from './utils/api';

const actioncreator = {
  bootstrap() {
    return (dispatch, getState) => {
      getSetup()
        .then(setup => dispatch({
            type: FETCH_SETUP,
            payload: setup
        }))
        .then(() => {
          // since `dispatch` is synchronous a call to `getState` will yield you the current state
          const subNode = getState().something.subNode;
          return fetchTranslations(subNode);
        })
        .then(translations => dispatch({
            type: FETCH_TRANSLATIONS,
            payload: translations
          }))
        .then(fetchOffers)
        .then(offers => dispatch({
          type: FETCH_OFFERS,
          payload: offers
        }));
    }
  }
}

It's not tested but you get the idea.

StevenLangbroek commented 9 years ago

Right, ok, that makes sense. I've been toying with this, but the next problem is that the fetchOffers action is also a stand-alone action, so it's curried, and should receive dispatch, getState in the 2nd function...

alkhe commented 9 years ago

@StevenLangbroek If it doesn't fit the dispatch format, you can always just write a wrapper to do it for you. It's not optimal, but that's how most people do it. Or convince management/upstream to change it (less likely to be successful).

itsmepetrov commented 9 years ago

You can use redux-combine-actions for sequential actions. It will look something like this:

export function fetchData() {

    return {
        types: [
            'COMBINED_ACTION_START',
            'COMBINED ACTION_SUCCESS',
            'COMBINED ACTION_ERROR'
        ],

        // Set true for sequential actions
        sequence: true,

        // Pass actions in array
        payload: [getSetup, fetchTranslations, fetchOffers]
    };
}

This will dispatch actions in the following sequence: _COMBINED_ACTION_START -> FETCH_SETUP -> FETCH_TRANSLATIONS -> FETCH_OFFERS -> COMBINED_ACTION_SUCCESS_

nickdima commented 9 years ago

@itsmepetrov your middleware looks interesting. Could you update the readme explaining how to apply it , how does the types array work, etc.? Thanks!

itsmepetrov commented 9 years ago

@nickdima I've updated documentation for the middleware and I hope docs will answer your questions. But I'm always glad to help.

nickdima commented 9 years ago

@itsmepetrov much better! Thanks!

gaearon commented 9 years ago

Closing, as seems resolved by either redux-combine-actions or manual action creator composition with redux-thunk.