redux 提供了類似后端 Express 的中間件概念。
最適合擴展的是redux中的 store.dispatch 方法,中間件實際就是通過 override redux的store.dispatch() 完成
將 action -> reducer 過程變為 action -> middlewares -> reducer 如:
let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}
添加中間件
redux提供了 applyMiddleware 方法便於添加中間件
applyMiddleware的源碼:
export default function applyMiddleware(...middlewares) {
middlewares = middlewares.slice()
middlewares.reverse()
// Transform dispatch function with each middleware.
middlewares.forEach(middleware =>
// 由於每次middle會直接返回返回函數,然后在這里賦值給store.dispatch,
// 下一個middle在一開始的時候,就可以通過store.dispatch拿到上一個dispatch函數
store.dispatch = middleware(store)
)
}
通過middleware將 store.dispatch 進行擴展
middleware會返回一個函數:return store => dispatch => action => {}
異步操作
- 同步操作只要發出一種 Action 即可,異步操作通常需要發出多種 Action(開始、成功、失敗)
- 異步操作有2種Action寫法(3種type 或者 添加erro 和 response字段)
- 異步操作的state結構調整
Action寫法:
// 寫法一:名稱相同,參數不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
// 寫法二:名稱不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }
異步State結構:
let state = {
// ...
isFetching: true,// 正在獲取數據
didInvalidate: true,// 是否過期
lastUpdated: 'xxxxxxx'// 上次更新時間
};
使用中間件
- 使用
createStore(reducer, enhancer)
或createStore(reducer, preloadedState, enhancer)
- applyMiddleware的參數為中間件,某些中間件有順序要求如:logger
redux-logger
redux-logger 可清晰記錄 preState action nextState time等信息。
示例:
import { createStore, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import rootReducer from './reducers'
let store = createStore(rootReducer, applyMiddleware(createLogger));
redux-thunk
redux-thunk 用來優化redux中的異步操作。
在store.dispatch 的方法參數只支持js對象(即Action),使用redux-thunk將支持參數為一個函數。
或者說 redux-thunk 使得 action 從一個對象變成一個函數。
函數簽名:(dispatch, getState) => {}
示例:
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import rootReducer from './reducers'
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
store.dispatch( dispatch => {
dispatch({type:'CLICK_START',data:res})
fetch('xx')
.then(res => dispatch({type:'CLICK_END',data:res}));
} )
實際上,redux-thunk 的作用是讓 Action Creator方便可以返回函數,這樣讓項目中的同步異步Action Creator調用可以保持一致
redux-saga
redux-saga 相比thunk 功能顯得全面,精細。
saga 是一個常駐進程,在復雜任務 及 長時事務場景非常適用。
這里通過2個action 來展示 saga
示例:
import createSagaMiddleware, { delay } from 'redux-saga'
import { all, put, takeEvery, takeLatest } from 'redux-saga/effects'
function* helloSaga() {
yield delay(1000)
console.log('hello world')
}
function* incrementAsync() {
yield delay(1000)
yield put({ type: 'click' })
}
function* rootSaga() {
yield all([
takeEvery('hello', helloSaga),
takeLatest('async', incrementAsync)
])
}
let sagaMiddleware = createSagaMiddleware();
let store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga);
store.dispatch({ type: 'hello' });
store.dispatch({ type: 'async' });
支持異步的還有redux-promise 等