Exploration of Redux Middleware

Time:2021-9-3

Thinking from Redux thunk

When using Redux thunk to write asynchronous actions, I often wonder how Redux works and makes asyncaction possible

In order to explore, we must take a look at the source code of Redux thunk. Fortunately, the source code of Redux thunk is very few… As for why, let’s explain it immediately.

Redux thunk source code

// redux-thunk source code
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

It can be seen from the source code that the middleware is only a factory function, which outputs a factory function of nested factory functions. The final parameter with the return function of next is the middleware that Redux needs to adapt to.

It may be troublesome to use ES6 arrow syntax. We can try to convert this code directly to Es5.

function createThunkMiddleware(extraArgument) {
  return function (storeOrFakeStore) {
    var dispatch = storeOrFakeStore.dispatch;
    var getState = storeOrFakeStore.getState;
    return function (next) {
      return function (action) {
          return action(dispatch, getState, extraArgument);
      }
      return next(action);
    }
  };
}

It can be seen from the source code that the middleware itself will accept the current store or a fakestore (the fakestore may only host the two store APIs, dispatch and getstate), and pass the methods of dispatch and getstate into the action function that may perform asynchronous operations. In this way, after the action completes the asynchronous operation, it is also given the right to dispatch, and the state can be transferred to the next scenario through the action.

Then the students will ask again, what is this next? Well, in fact, this next is the middleware to deal with action next. After all, middleware is one after another, right. If you have written koa or express, you should be familiar with the next.

Next, let’s take a look at how the createstore module in the react source code applies middleware.

What happens when we create a store using the createstore API

Applymeddleware is also a factory. If you have used Redux middleware, you should know how the Redux store is created

const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

The following is the source code of createstore. We will only look at the relevant part

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}

Combined with the declaration and use of store, we can know that the third parameter of Redux can accept something called an enhancer. If there is an enhancer, call the enhancer method directly to return a new store and enhance the function of redux. When using middleware, Redux uses the existing applymeddleware factory method as an enhancer on redux.

How the applymiddleware API implements middleware operations.

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

The applymiddleware factory function composes the incoming middleware so that the middleware is nested with each other, so that the next function in the middleware can be executed by next()… The new dispatch will be triggered from the first middleware, so that when we call store.dispatch, we will go through the middleware.

//Compose function
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }
  //If there are multiple middleware, directly use the reduce method to nest each middleware.
  //Therefore, we should pay attention when using middleware. The essence of middleware is an interception operation
  //If two middleware intercepts a certain type of action successively, we must pay attention to it
  //The order of inserting Middleware in createstore, and the execution of middleware methods is orderly.
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

You can also make a middleware

How about Redux? Is the source code simple? It’s so simple that students can develop middleware by themselves. Now you can write your own react middleware by yourself.

Recommended Today

SQL exercise 20 – Modeling & Reporting

This blog is used to review and sort out the common topic modeling architecture, analysis oriented architecture and integration topic reports in data warehouse. I have uploaded these reports to GitHub. If you are interested, you can have a lookAddress:https://github.com/nino-laiqiu/TiTanI recorded a relatively complete development process in my hexo blog deployed on GitHub. You can […]