There are some scenarios in which we need to write middleware to implement certain functions, but there are already some good middleware out there in the active community, so even though we know the writing principles of middleware can be encapsulated ourselves, However, sometimes we need to use middleware already written by others for convenience. Here are a few commonly used third-party middleware

redux-logger

In the last article (Redux Middleware for React Learning), we wrote our own middleware with the requirement points for printing logs, and we already had the required logging functionality. We don’t need to write this function every time we develop a project (or write a tool library into the NPM library), because redux-Logger is already implemented for us, and we only need to use it in the development phase, and it has more features and better log style. So we can use it directly (the implementation principle and simple implementation code can be seen in the previous article) :

import {createStore, applyMiddleware} from 'redux'
import logger from 'redux-logger'
import reducer from '@/store/combine-reducer'

const store = createStore(
    reducer,
    applyMiddleware(logger) /* Apply middleware directly */
)

export default store
Copy the code

See the official API description for more usage

Note: When used with Redux-Thunk or another middleware library that can handle side effects, use logger middleware as the last argument to applyMiddleware. Otherwise it will not work due to action (see the Redux-Thunk principle below for details)

redux-thunk

As we’ve seen before, there can be no side effects in Redux (such as requests for data or other asynchronous operations), and the action distributed must be a plain flat object with a Type attribute

// So before we request the data, we distribute the action to update the data in redux
// The operation can only be handled this way.
import {getAllUsers} from '@/service/users'
import store from '@/store'
import {createSetUsersAction} from '@/store/action'

getAllUsers().then(res= > {
    // res is the requested user[]
    // The data request is successful, then distribute the action
    store.dispatch(createSetUsersAction(res))
})

// @/store/action.js ➡️ action create function (return action plane object)
const SETUSERS = Symbol('setUsers')
export const createSetUsersAction = (payload) = > ({
    type: SETUSERS,
    payload
})
Copy the code

When we use thunk, we can handle side effects in the action creator, which will return a side effect function

Story – thunk usage:

import {createStore, applyMiddleware} from 'redux'
import ReduxThunk from 'redux-thunk'
import logger from 'redux-logger'
import reducer from '@/store/combine-reducer'

// We need to attach extra parameters manually (type any)
// ReduxThunk.withExtraArgument(extraArg)

// Application middleware
const store = createStore(
    reducer,
    applyMiddleware(
        ReduxThunk, / * replaced with ReduxThunk. WithExtraArgument (extraArg) * /
        logger
    )
)

// @/store/action.js ➡️ action create function (return side effect operator function)
export const createSetUsersAction = () = > {
    // thunk will combine the dispatch function with the getState method
    // We need to pass in an extra argument.
    return async (dispatch, getState, extraArg) => {
        // Add an action ➡️ true to the loading state
        const res = await getAllUsers()
        dispatch(setUsersAction(res)) // Distribute the users action
        // Action ➡️ false in loading state is distributed}}/ / use
import {createSetUsersAction} from '@/store/action'
import store from '@/store'

store.dispatch(createSetUsersAction()) // Request data and set data
Copy the code

Story – thunk principle

Thunk allows an action to be a function with side effects. When an action is distributed as a function, the Thunk middleware prevents the action from being handed over and calls the function directly. If it is a plain Action flat object, it is handed over later

Once you understand how to implement it, you’ll find that its code implementation is very simple:

/ / create a story - thunk. Js
const createThunkMiddleware = extraArg= > {
    // Return to thunk middleware
    return store= > next= > action= > {
        if (typeof action === 'function') {
            return action(store.dispatch, store.getState, extraArg)
        }
        return next(action)
    }
}

const thunk = createThunkMiddleware()
thunk.withExtraArgument = createThunkMiddleware
export default thunk
Copy the code

redux-promise

The redux-Thunk middleware above allows us to distribute an action as a function with side effects, so we know from the redux-Promise name that it allows us to distribute an action as a promise

Story – promise usage:

import {createStore, applyMiddleware} from 'redux'
import ReduxPromise from 'redux-promise'
import logger from 'redux-logger'
import reducer from '@/store/combine-reducer'

// Application middleware
const store = createStore(
    reducer,
    applyMiddleware(
        ReduxPromise,
        logger
    )
)

// store/action.js
// Or use async (essentially Promise candy)
// Approach 1: The distributed action is itself a Promise object
export const createSetUsersAction = () = > {
    return new Promise((resolve, reject) = > {
        // After simulating 3s, get data and distribute action
        setTimeout(() = > {
            resolve({
                type: actionTypes.setUsersAction,
                payload: 'any data... '})},3000)})}// Method 2: The distributed action is a normal object, but the payload is a Promise object
export const createSetUsersAction = () = > {
    return {
        type: actionTypes.setUsersAction,
        payload: new Promise(resolve= >{resolve (' any data... ')}}}Copy the code

As you can see from the above example, an action of type Promise is distributed through the resolve function

Payload is a Promise object, and the result of the rejected action is processed only if the action. Payload is a Promise object

Story – promise principle

The Promise middleware does the following:

  1. Determine if an action is a standard Flux Action object (plain flat object; Must have a type attribute name and type String; Remaining optional attribute names [“payload”, “error”, “meta”]) (with isFSA(action))

  2. If not, then check whether the action is a Promise(isPromise(action)). If the action is a Promise, then action. Actions that were dispatched as dispatch functions as a result of resolved; If the action is not a Promise object, the dispatch of the next middleware is called directly

  3. If action is a standard Flux action, check whether action.payload is a Promise object (isPromise(action.payload)). Payload = rejected (payload = error)

The code implementation is as follows:

// @/redux-promise/index.js
import {isFSA} from 'flux-standard-action'
import isPromise from 'is-promise'

const promiseMiddleware = ({dispatch}) = > next= > action= > {
    /** * Check whether it is a standard Flux Action */
    if(! isFSA) {/** * if the action is a promise, then the action is a promise
        return isPromise(action) ? action.then(dispatch) : next(action)
    }
    // Is a standard Flux Action
    /** * the payload is a Promise object. Payload = / / payload = / / payload = / / payload = / / payload = / / If no, transfer to */
    return isPromise(action.payload) ?
        action.payload
          .then(payload= >dispatch({ ... action, payload })) .catch(error= >{ dispatch({ ... action,payload: error })
              return Promise.reject(error)
          })
        : next(action)
}

export default promiseMiddleware
Copy the code

redux-saga

Both redux-thunk and Redux-Promise lead to actions or action-created functions that are not pure, but are contrary to the purity of Redux itself. Redux-saga solves this problem by not only keeping action pure, but also by using a modular approach to side effects and powerful functionality

The Redux-Saga middleware is built on ES6 generators, and to understand generators, you need to understand iterators and iterable protocols, so let’s review them in turn

Go to ➡️ JavaScript iterators and generators 🤓

The characteristics of the saga

Pure, powerful and flexible

todo…