Redux入門0x104: redux 中介軟體
阿新 • • 發佈:2018-12-11
0x000 概述
前一章講了redux
的Action Creator
,這一章講redux
中很神奇的中介軟體。
0x001 手寫中介軟體
在專案中,我們經常會有記錄一些事件或者在某些事件發生的時候做某些事的需求,比如api
介面鑑權操作、日誌記錄操作等,一般我們都可以用中介軟體來完成,中介軟體具有可拔插、可擴充套件的特點。我們也可以使用中介軟體來擴充套件redux
的功能。
-
記錄每一次的
action
和state
變化我們在之前是這麼使用redux
的:import {createStore} from 'redux' function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 default: return state } } let store = createStore(counter) store.subscribe(() => { // console.log(store.getState()) }) const ACTION_INCREMENT = 'INCREMENT' const increment = () => { return { type: ACTION_INCREMENT } } const action = increment() store.dispatch(action) store.dispatch(action) store.dispatch(action)
通過
store.dispatch
完成對資料的修改,現在我們希望記錄每一次對資料的修改,我們可以這麼做console.log('action', action.type) store.dispatch(action) console.log('next state', store.getState()) console.log('action', action.type) store.dispatch(action) console.log('next state', store.getState()) console.log('action', action.type) store.dispatch(action) console.log('next state', store.getState())
效果很明顯,豬才會這麼做
封裝1:封裝成函式,可以,經常我們也是這麼做的,但是不好
const dispatch = (store, action) => { console.log('action', action.type) store.dispatch(action) console.log('next state', store.getState()) } dispatch(store, action)
封裝2:
hack
掉store.dispatch
,沒毛病,但是不夠優雅並且如果希望有多箇中間件不太好辦,並且希望中間鍵可以串聯起來const storeDispatch=store.dispatch store.dispatch= (action) => { console.log('action', action.type) storeDispatch(action) console.log('next state', store.getState()) } store.dispatch(action)
-
封裝3:多箇中間件串聯
這裡寫了兩個中間鍵,一個是前中介軟體,一個是後中介軟體,在執行
before(store)
的時候,其實我們已經將store.dispatch
替換成了before
的dispatch
,所以我們在after
對dispatch
第二次替換的時候,const storeDispatch = store.dispatch
中的store.dispatch
其實是before.dispatch
,所以,當我們執行store.dispatch(increment())
的時候,呼叫鏈其實是:store#dispatch=after#dispatch -> before#dispatch -> before#console.log -> store#dispatch -> after#console.log
const before = (store) => { const storeDispatch = store.dispatch const dispatch=(action) => { console.log('before', action.type,store.getState()) storeDispatch(action) } store.dispatch = dispatch } const after = (store) => { const storeDispatch = store.dispatch const dispatch = (action) => { storeDispatch(action) console.log('after',action.type,store.getState()) } store.dispatch=dispatch } before(store) after(store) store.dispatch(increment())
檢視輸出:
-
封裝4:隱藏
hack
,減少樣板程式碼const before = (store) => { const storeDispatch = store.dispatch return (action) => { console.log('before', action.type, store.getState()) storeDispatch(action) } } const after = (store) => { const storeDispatch = store.dispatch return (action) => { storeDispatch(action) console.log('after', action.type, store.getState()) } } const applyMiddleware = (store, ...middlewares) => { middlewares.reverse() middlewares.forEach(middleware => { store.dispatch = middleware(store) }) } applyMiddleware(store, before, after) store.dispatch(increment())
-
封裝5:不使用 hack
const before = (store) => { return (storeDispatch) => { return (action) => { console.log('before', action.type, store.getState()) storeDispatch(action) } } } const after = (store) => { return (storeDispatch) => { return (action) => { storeDispatch(action) console.log('after', action.type, store.getState()) } } } const applyMiddleware = (store, ...middlewares) => { middlewares.reverse() let storeDispatch = store.dispatch middlewares.forEach(middleware => { storeDispatch = middleware(store)(storeDispatch) }) // store.dispatch = storeDispatch return {...store, ...{dispatch: storeDispatch}} } store = applyMiddleware(store, before, after) store.dispatch(increment())
-
封裝6:優化中介軟體寫法
const before = store => storeDispatch => action => { console.log('before', action.type, store.getState()) return storeDispatch(action) } const after = store => storeDispatch => action => { let result = storeDispatch(action) console.log('after', action.type, store.getState()) return result }
-
最終的完整程式碼
import {createStore} from 'redux' // reducer function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 default: return state } } // 建立 store let store = createStore(counter) // action const ACTION_INCREMENT = 'INCREMENT' // action creator const increment = () => { return { type: ACTION_INCREMENT } } // 前中介軟體 const before = store => storeDispatch => action => { console.log('before', action.type, store.getState()) return storeDispatch(action) } // 後中介軟體 const after = store => storeDispatch => action => { let result = storeDispatch(action) console.log('after', action.type, store.getState()) return result } // 應用中介軟體 const applyMiddleware = (store, ...middlewares) => { middlewares.reverse() let storeDispatch = store.dispatch middlewares.forEach(middleware => { storeDispatch = middleware(store)(storeDispatch) }) // store.dispatch = storeDispatch return {...store, ...{dispatch: storeDispatch}} } // 返回了新的 store store = applyMiddleware(store, before, after) // 發出 action store.dispatch(increment())
-
0x002 redux applyMiddleware
前面寫了一個applyMiddleware
方法,雖然可以用,但是官方其實也提供了這個方法,並且比我們寫的更好一點
const before = store => storeDispatch => action => {
console.log('before', action.type, store.getState())
return storeDispatch(action)
}
const after = store => storeDispatch => action => {
let result = storeDispatch(action)
console.log('after', action.type, store.getState())
return result
}
let store = createStore(counter, applyMiddleware(before, after))
store.dispatch(increment())
可以看出來,相較於我們自己寫的`applyMiddleware`,官方提供的可以直接傳遞給`createStore`,而無需在次對`store`進行操作。
0x003 非同步action
const before = store => storeDispatch => action => {
console.log('before', action.type, store.getState())
return storeDispatch(action)
}
const after = store => storeDispatch => action => {
let result = storeDispatch(action)
console.log('after', action.type, store.getState())
return result
}
const asyncAction=()=>{
return (dispatch)=>{
setInterval(()=>{
dispatch(increment())
},1000)
}
}
const asyncMiddleware = store => storeDispatch => action => {
if (typeof action === "function") {
return action(storeDispatch)
} else {
return storeDispatch(action)
}
}
let store = createStore(counter, applyMiddleware(asyncMiddleware,before,after))
store.dispatch(asyncAction())
這裡寫了一個asyncMiddleware
,他判斷傳入的action
是否是一個函式,如果是一個函式,那就直接執行這個函式,同時將dispatch
作為引數,則在asyncAction
我們就能直接訪問到dispatch
了,就可以在asyncAction
適當的時候再次dispatch
了。當然一樣的,這樣的中介軟體也已經存在了:redux-thunk,日誌的中介軟體也已經存在了:redux-logger
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
const store = createStore(
counter,
applyMiddleware(
thunkMiddleware,
createLogger()
)
)
store.dispatch(increment())
檢視效果