为了更好的阅读体验,你可以点击这里查看github原文
何为中间件
在express
中,它的中间件可以访问请求对象、响应对象和next
,即下一个中间件;express
通过组合各种中间件和路由构造应用。而在redux
中,从派发action
到将action
传入reducer
这个过程中间(可以理解为dispacth
时)可以有一个管道,在这个管道中可以包含若干个中间件,用于处理同步或异步的action
。每个中间件都是独立的函数,可以组合使用,每个中间件都有统一的接口。之所以将这个过程称之为管道是因为,这些中间件并非并发的,你可以将action
看作一个数据流,它依次通过各个中间件,当action
在某个中间件中断后,后面的中间件也不会再进行任何操作,当然也不会进入到reducer
中。
构造一个中间件
理解了上面的中间件的作用和它的机制之后,我们来手动实现一个中间件!他需要返回一个接受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
上的两个同名函数的对象,dispatch
和getState
。所以一个中间件拥有以下几种功能:
-
dispatch
派发新的action
对象 - 调用
getState
获取当前redux store
上的状态 - 调用
next
,告诉redux
当前中间件工作完毕,让redux
调用下一个中间件 - 访问
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 }
}
网友评论