Skip to content

Chainable asynchronous middlewares #1584

Closed
@recidive

Description

@recidive

Hello, we are trying to create modules made of a redux reducer and/or a middleware. The modules are to be reused and distributed, mostly like Ducks does, but also supporting middlewares.

We are using redux actions and trying to do something useful asynchronously, like reading a file or something from the database, and also allow a chain of dependency where modules' middlewares/reducers can rely on the state/payload of the module it depends on, and use it to build and return a new state.

Something like:

module 1
-> dispatch action to read something async
-> query database middleware await data from database
-> run all other modules (async/sync) middlewares
-> reducer consumes data and build a new state
-> then it's passed to module 2 reducer and so on...

For doing so we are trying to run through a chain of (synchronous and asynchronous) side-effects in middlewares.

We don't want to do anything other than returning the FSA object when defining actions, so we don't restrict the data gathering logic and any other business logic only to the module that creates the action and centralize them in middlewares.

import {createStore, applyMiddleware} from 'redux'
import {createAction, handleActions} from 'redux-actions'
import promiseMiddleware from 'redux-promise'

const BOOT = 'BOOT'
const AFTER_BOOT = 'AFTER_BOOT'
const AFTER_AFTER_BOOT = 'AFTER_AFTER_BOOT'

const someApi = {
 get(id) {
   return Promise.resolve({
     name: 'yeah'
   })
 }
}

const bootAction = createAction(BOOT)

const afterBootAction = createAction(AFTER_BOOT, async (id) => {
 const result = await someApi.get(id)
 return result
})

const afterAfterBootAction = createAction(AFTER_AFTER_BOOT, async (id) => {
 const result = await someApi.get(id)
 return result
})

const initialState = {foo: 'bar'}

const reducer = (state = initialState, action) => {
 switch (action.type) {
   case AFTER_BOOT:
     return {
       ...state,
       foo: 'baz'
     }

   case AFTER_AFTER_BOOT:
     return {
       ...state,
       foo: 'rock'
     }
   default:
     return state
     break
 }
}

const middlewares = [
 function middle({getState, dispatch}) {
   // Note the async keyword.
   return next => async (action) => {

     if (action.type == BOOT) {
       const sideffectA = await dispatch(afterBootAction(2))
     }

     if (action.type == AFTER_BOOT) {
       const sideffectB = await dispatch(afterAterBootAction(3))
     }

     return next(action)
   }
 },
 promiseMiddleware
]

const store = createStore(reducer, initialState, applyMiddleware(...middlewares))

// First action, and should fire other side-effects.
store.dispatch(bootAction())

console.log(store.getState(), "It should print {foo: 'rock'}")

We are doing this as part of our Choko Redux project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions