简体   繁体   中英

How to manage (async) cascading data in Redux?

Imagine a car configuration app. Here is an example state tree:

{
  // selections
  modelYear,
  modelId,
  transmissionId,

  // options
  modelYears: [],
  models: [],
  transmissions: [],
  accessories: []
}

When the app starts it would load some initial data. Then you would start by selecting a model year to populate the list of models, then select a model, then a transmission, accessories, etc. Each piece of data relies on the previous selection in a hierarchical manner. An example sequence of events on page load would be the following:

  1. Retrieve all years and populate a dropdown list.
  2. Select the first year by default
  3. Retrieve models based on the selected year
  4. Select the first model by default
  5. Retrieve transmissions based on the selected model
  6. Select a transmission by default
  7. Retrieve accessories based on the selected model etc...

When the user changes a selection of any piece of the data, new data lower in the hierarchy has to be retrieved and the UI has to be repopulated.

Here are some snippets of what I have written:

actions

export function getModelYears() {
  return dispatch => {
    vehiclesApi.getModelYears(years => {
      dispatch(receiveModelYears(years))
    })
  }
}

export function getModels(year) {
  return dispatch => {
    vehiclesApi.getModels(year, models => {
      dispatch(receiveModels(models))
    })
  }
}

function receiveModelYears(years) {
  return {
    type: types.RECEIVE_MODEL_YEARS,
    years: years
  }
}

function receiveModels(models) {
  return {
    type: types.RECEIVE_MODELS,
    models: models
  }
}

export function selectModelYear(year) {
  return {
    type: types.SELECT_MODEL_YEAR,
    year
  }
}

reducers

function vehicle(state = {}, action) {
  switch (action.type) {
    case SELECT_MODEL_YEAR:
      return Object.assign({}, state, {
        modelYear: action.year
      })
    case SELECT_MODEL:
      return Object.assign({}, state, {
        modelId: action.modelId
      })
    default:
      return state
  }
}  

function modelYears(state = [], action) {
  switch (action.type) {
    case RECEIVE_MODEL_YEARS:
      return state.concat(action.years)
    default:
      return state
  }
}

function models(state = [], action) {
  switch (action.type) {
    case RECEIVE_MODELS:
      return state.concat(action.models)
    default:
      return state
  }
}

I'm not sure how to structure my reducers & actions to handle the default selections and request new data after selections are made. The manual actions are easy (user clicks, dispatch action). But how do I manually dispatch getModels(year) after selecting the first year by default?

Here is the best way I can describe the flow in pseudo code:

store.dispatch(getModelYears())

store.subscribe(() => {
  // if model years received
  store.dispatch(selectModelYear(store.getState().modelYears[0]))

  // if modelYear selected
  store.dispatch(getModels(store.getState().modelYear))

  // if models received
  store.dispatch(selectModel(store.getState().models[0].id))

  // if model selected
  store.dispatch(getTransmissions(store.getState().modelId))
  store.dispatch(getAccessories(store.getState().modelId))

  // ...etc
})

I would like to test this system agnostic of any UI framework since I'm building it as a proof of concept for managing my client state and may not be able to use React.

If I understood your requirements correctly, you're looking for a way to respond to a state change by triggering another action. For example, whenever store.modelYear changes, you need to trigger the RECEIVE_MODELS action.

The subscribe method looks right for the job. If you want a more structured design, there are patterns built on top of subscribe , for example actors: http://jamesknelson.com/join-the-dark-side-of-the-flux-responding-to-actions-with-actors/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM