To implement a REdux middleware we must understand the basic implementation principles of Redux. This article will start with the redux source code and focus on how applyMiddleware executes middleware in tandem. Only by understanding the underlying principles can we easily write a Redux middleware.

directory

  • CreateStore source code interpretation
  • ApplyMiddle source code interpretation
  • Implementation principle of redux-Thunk
  • How to write a middleware
  • conclusion
  • The resources

CreateStore source code interpretation

Redux creates a store object through createStore

To understand how applyMiddleware works, start with createStore

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

  if (typeofenhancer ! = ='undefined') {
    if (typeofenhancer ! = ='function') {
      throw new Error('Expected the enhancer to be a function.')}return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeofreducer ! = ='function') {
    throw new Error('Expected the reducer to be a function.')}Redux redux redux redux redux redux
  / /...
Copy the code

We can see that the three parameters of createStore are reducer, preloadedState, enhancer. See the source code for passing in createStore to Enhance if the Enhance argument is a function

return enhancer(createStore)(reducer, preloadedState)

That is, we will now use Enhance to create a store object.

ApplyMiddlewave source code interpretation

Normally, the third parameter to createStore, Enhance, is applyMiddlewave

ApplyMiddlewave’s 20-odd lines of code are the focus of this article

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

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) = > dispatch(action)
    }
    chain = middlewares.map(middleware= >middleware(middlewareAPI)) dispatch = compose(... chain)(store.dispatch)return {
      ...store,
      dispatch
    }
  }
}
Copy the code

ApplyMiddlewave still creates a Store object using createStore and returns it, but overwrites the dispatch method.

Let’s focus on the rewritten dispatch method and understand how it differs from the native Dispatch method. To get a more intuitive understanding of this process, let’s first look at a simple middleware implementation called Logger MiddleWave

export default store => next= > action => {
    const start = Date.now();
    next(action);
    const ms = Date.now() - start;
    console.log(`dispatch: ${action.type} - ${ms}ms`);
}
Copy the code

The following two steps will discuss the operating principle of middleware in detail

  1. Native getState and Dispacth are passed into the middleware array as the first parameters to obtain the chain array.

    chain = middlewares.map(middleware => middleware(middlewareAPI))

  2. Combination Middlewave

    dispatch = compose(... chain)(store.dispatch)

    Compose connects all the middleware together to form the new Dispatch

    Compose source

    function compose(. funcs) {
        return arg= > funcs.reduceRight((composed, f) = > f(composed), arg);
    }
    Copy the code

    Referring to our Logger MiddleWave, composed is our next parameter.

    ReduceRight is the same as Ruduce, except that reduceRight is executed from the right end of the array, and arG will be used as the initial value of reduceRight (in this case, store.dispatch). Assuming that our chain array is [F1,f2,f3], the dispatch after execution is set to f1(f2(f3(store.dispatch)))), each middleware can execute in turn by calling this new dispatch. The middleware execution process here is also similar to Koa middleware which is a very classic Onion model. Only the last middleware will trigger redux’s native Dispatch to distribute the action. (Yes, I am a soul painter)

Implementation principle of redux-Thunk

Typically dispatch can only distribute one action object, but with the Redux-Thunk middleware we can distribute an asynchronous function.

const thunk = store= > next => action= > {
    typeof action === 'function' ?
        action(store.dispatch,store.getState) :
        next(action)
}
Copy the code

An example of asynchronous action

function getMessage = (dispatch, getState) = >{
    axios.get('xxx/xxx')
    .then((res) = > {
        dispatch({
            type: 'GET_MESSAGE_SUCCESS'.message: res.json(),
        });
    })
    .catch((err) = > {
        dispatch({
            type: 'GET_MESSAGE_ERROR'.message: 'error'
        });
    });
}
Copy the code

The dispatch here is still a modified Dispatch because the first parameter passed into the middleware store, the Dispatch in the middlewareApi, is a closure that holds a reference to the outermost function dispatch, So when Diapatch is rewritten, all subsequent dispatches are the new Dispatches (middleware cascades), so even distributing an action in an asynchronous action will still execute all the middleware again.

How to write a middleware

So with that in mind, it would be super easy to write a middleware, just to write the middleware specification

function myMiddleware = store= >next= >action = {
    // Here you can get store.getState and store.dispatch
    // Note that if you call the store.dispatch middleware and start from the outermost layer again, you will be stuck in an infinite loop if you do not restrict it
    // do somethingnext(action)// Go to the next middleware, the last one whose next parameter is Redux native Dispatch
    // return to continue executing the rest of the middleware
}
Copy the code

conclusion

An in-depth understanding of the implementation principle of the Redux middleware allows us to be more clear about the flow of Redux data and more confident about our own programs in our daily work. My level is limited, if there is any mistake, please point out.

The resources

Redux official documentation

Deep in the React Tech Stack

Ruan Yifeng Redux tutorial

Original article, reprint please indicate the original address

If you like, can you give me a careful heart (* ^ __ ^ *) hee hee……