redux中间件和store enhancer

作者: mytac | 来源:发表于2019-01-22 18:39 被阅读6次

为了更好的阅读体验,你可以点击这里查看github原文

何为中间件

express中,它的中间件可以访问请求对象、响应对象和next,即下一个中间件;express通过组合各种中间件和路由构造应用。而在redux中,从派发action到将action传入reducer这个过程中间(可以理解为dispacth时)可以有一个管道,在这个管道中可以包含若干个中间件,用于处理同步或异步的action。每个中间件都是独立的函数,可以组合使用,每个中间件都有统一的接口。之所以将这个过程称之为管道是因为,这些中间件并非并发的,你可以将action看作一个数据流,它依次通过各个中间件,当action在某个中间件中断后,后面的中间件也不会再进行任何操作,当然也不会进入到reducer中。

demo

构造一个中间件

理解了上面的中间件的作用和它的机制之后,我们来手动实现一个中间件!他需要返回一个接受next参数的函数,而这个函数又需要返回一个接受action的函数。ok,我们将上述的过程,用箭头函数解释出来:(至于为什么这样设计,后面将给出解释)

const doNothingMiddleWare = ({ dispatch, getState }) => next => action => next(action);

如果你对函数式编程不敏感,用普通函数将他翻译为:

function doNothingMiddleWare({dispatch, getState}){
    return function (next){
        return function (action){
            return next(action)
        }
    } 
}

比如,我们需要一个打印action type的中间件,

const loggerMiddleWare = ({ dispatch, getState }) => {
    return next => {
        return action => {
            console.log('action', action)
            return next(action)
        }
    }
}

中间件函数接收一个包含redux store上的两个同名函数的对象,dispatchgetState。所以一个中间件拥有以下几种功能:

  1. dispatch派发新的action对象
  2. 调用getState获取当前redux store上的状态
  3. 调用next,告诉redux当前中间件工作完毕,让redux调用下一个中间件
  4. 访问action对象上的所有数据

因为redux的设计是基于函数式编程的,所以中间件中的函数尽可能小,并且可以进行组合。如果不是出于函数式编程,中间件可以写成这样:

function middleWare({ dispatch, getState },next){
    return function(action){
        return next(action)
    }
}

调用中间件

调用中间件有两种方法

1. 使用applyMiddleware包装createStore产生新函数

调用这个中间件:

import logMiddleWare from './reduxMiddleWare/logger'
// ...
const configureStore = applyMiddleware(logMiddleWare)(createStore); // store enhancer creator
const store = configureStore(reducer,initialState)
// ...

在每次dispatch的时候,我们将会在控制台看到打印的信息:

action {type: Symbol(FETCHING_START)}
action {type: Symbol(FETCHING_SUCCESS), result: {}}

2. 多个store enhancer混合

上面的处理方式适用于只有一个store enhancer的情况,如果需要多个store enhancer需要用compose进行组合,如下:

const enhancers=compose(applyMiddleware(logMiddleWare,...otherMiddleWares),devToolsExtensions()) // devToolsExtensions是redux-devtools的增强器
const store=createStore(reducer,enhancers)

切记,compose中中间件的顺序是action传入的顺序,如果applyMiddleware(logMiddleWare,...otherMiddleWares)放在devToolsExtensions之后,异步的action将不会被捕获,可能会抛出异常。

何为store enhancer

Promise中间件

在应用中,对于请求数据一些操作,我们可以通过派发异步action进行操作,所以我们可以设计一个Promise中间件来实现这样的操作。

const isPromise=p=>(p&&p.then&&(typeof p.then==='function'))

function promiseMiddleWare({dispatch}){
    return  next=>{
        return action=>{
            return isPromise(action)?action.then(dispatch):next(action)
        }
    }
}

比如在请求数据的时候,有三个请求状态,分别为pending、success、fail;然后我们需要给每个promise对象,绑定这三个状态。

function promiseMiddleWare(){
    return ({dispatch,getState})=>{
        return next=>{
            return action=>{
                if(!isPromise(action)||!(action.types&&action.types.length===3)){
                    return next(action)
                }
                const {PENDING,SUCCESS,FAIL}=action.types
                dispatch({type:PENDING})
                return action.promise
                .then(result=>dispatch({type:SUCCESS,result}))
                .catch(err=>dispatch({type:FAIL,err}))
            }
        }
    }
}

在使用fetch进行数据请求时,我们就可以这样定义action

{types:[PENDING,SUCCESS,FAIL],promise:fetch(api)}

store enhancer

中间件可以用来增强dispatch方法,但store enhancer可以对redux store进行更深层的定制。上文中的applyMiddleware就是一个store enhancer。比如上文我们使用的logMiddleWare是这样写的:

const loggerMiddleWare = ({ dispatch, getState }) => {
    return next => {
        return action => {
            console.log('action', action)
            return next(action)
        }
    }
}

如果我们在store上,直接在dispatch方法上挂载log方法。

const logEnhancer = createStore => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    const oldDispatch = store.dispatch
    store.dispatch = (action) => {
        console.log('dispatch action:', action)
        oldDispatch(action)
    }
    return store
}

// 调用
const enhancers = compose(logEnhancer, applyMiddleware(reduxThunk, promiseMiddleWare))
const store = createStore(reducer, enhancers)

一个store对象上包含以下接口,我们可以对其进行扩展,但仍然不能直接修改state,需要用dispatch派发。

dispatch
subscribe
getState
replaceReducer

如下例,我们可以在store上添加一个reset方法:

const resetCreator = (reducer, resetState) => (reducer, resetState) => (state, action) => {
    if (action.type === 'reset') {
        return resetState
    }
}

const reset = createStore => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    const reset = (resetReducer, resetState) => {
        const newReducer = resetCreator(resetReducer, resetState)
        store.replaceReducer(newReducer)
        store.dispatch({ type: 'reset', state: resetState })
    }

    return { ...store, reset }
}

参考链接

  1. 浅析Redux 的 store enhancer

最后请大家关注我的订阅号获得更加及时的推送~

那屋水泡

相关文章

网友评论

    本文标题:redux中间件和store enhancer

    本文链接:https://www.haomeiwen.com/subject/eqyrjqtx.html