Redux中的Enhancer 与 Middleware
1. store enhancer
- Store Enhancer基本结构
Store Enhancer是一个函数,这个函数可以接受一个createStore模样的函数为参数,返回一个新的createStore函数。它的主要目的是改变和加强store的接口方法。
const doNothingEnhancer = (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, prloadedStat, enhancer);
return store;
};
上面的例子中,最里层的函数体可以拿到一个createStore函数对象,还有应该传递给这个creatStore函数对象
的三个参数。所以基本的套路就是利用所给的参数创造出一个store对象,然后定制store对象,最后把store
对象返回出去。
- 实现Store Enhancer
实现一个Store Enhancer, 在于如何定制产生的Store对象,一个store对象包含下列接口:
dispatch, subscribe, getState, replaceReducer, 每一个接口都可以被修改,无论怎么修改最后
都要调用原有对应的函数。
记录action
const logEnhancer = (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, prloadedStat, enhancer);
const originalDispatch = store.dispatch;
store.dispatch = (action) => {
console.log('dipatch action: ', action);
originalDispatch(action);
}
return store;
};
Store Enhancer通常都使用这样的模式,将store上某个函数的引用保存下来,给这个函数一个新的实现,
但是在完成增强功能之后,还是要调用原有的函数,保持原有的功能。
在store中增加reset方法
const RESET_ACTION_TYPE = '@@RESET';
const resetReducerCreator = (reducer, resetState) => (state, action) => {
if (action.type === RESET_ACTION_TYPE){
return resetState;
} else {
return reducer(state, action);
}
}
const reset = (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer);
const reset = (resetReducer, resetState) => {
const newReducer = resetReducerCreator(resetReducer, resetState);
store.replaceReducer(newReducer);
store.dispatch({type: RESET_ACTION_TYPE, state: resetState});
};
return {
...store,
reset
};
}
export default reset;
- 使用Store Enhancer
import { createStore, compose } from 'redux'
import rootReducer from './reducer'
import {
sayHiOnDispatch,
includeMeaningOfLife
} from './exampleAddons/enhancers'
const composedEnhancer = compose(sayHiOnDispatch, includeMeaningOfLife)
const store = createStore(rootReducer, composedEnhancer)
export default store
2. 中间件(Middleware)
Middleware是一种特殊的enhancer,旨在加强和改变dispatch的行为,在action到达reducer之前提供
扩展功能,比如进行异步或者其他副作用操作。
中间件接口:
function doNothingMiddleware({dispatch, getState}) {
return function(next){
return function(action){
return next(action);
}
}
}
在中间件函数里可以:
-
调用dispatch派发一个action对象
-
调用getState获得当前Redux Store 上的状态
-
调用next告诉Redux当前中间件工作完毕,让Redux调用下一个中间件
-
访问action对象action上的所有数据
-
用中间件创建store
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducer'
import { print1, print2, print3 } from './exampleAddons/middleware'
const middlewareEnhancer = applyMiddleware(print1, print2, print3)
// Pass enhancer as the second arg, since there's no preloadedState
const store = createStore(rootReducer, middlewareEnhancer)
export default store
- 编写自定义中间件
ES5 functions
// Outer function:
function exampleMiddleware(storeAPI) {
return function wrapDispatch(next) {
return function handleAction(action) {
// Do anything here: pass the action onwards with next(action),
// or restart the pipeline with storeAPI.dispatch(action)
// Can also use storeAPI.getState() here
return next(action)
}
}
}
ES6 arrow functions
const anotherExampleMiddleware = storeAPI => next => action => {
// Do something in here, when each action is dispatched
return next(action)
}
3. 异步函数中间件
- 自定义异步函数中间件
const asyncFunctionMiddleware = storeAPI => next => action => {
// If the "action" is actually a function instead...
if (typeof action === 'function') {
// then call the function and pass `dispatch` and `getState` as arguments
return action(storeAPI.dispatch, storeAPI.getState)
}
// Otherwise, it's a normal action - send it onwards
return next(action)
}
const middlewareEnhancer = applyMiddleware(asyncFunctionMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)
// Write a function that has `dispatch` and `getState` as arguments
const fetchSomeData = (dispatch, getState) => {
// Make an async HTTP request
client.get('todos').then(todos => {
// Dispatch an action with the todos we received
dispatch({ type: 'todos/todosLoaded', payload: todos })
// Check the updated store state after dispatching
const allTodos = getState().todos
console.log('Number of todos after loading: ', allTodos.length)
})
}
// Pass the _function_ we wrote to `dispatch`
store.dispatch(fetchSomeData)
// logs: 'Number of todos after loading: ###'
- 使用redux-thunk
redux-thunk 实现:
function createThunkMiddleware(extraArguments){
return ({dispatch, getState}) => next => action => {
if(typeof action === 'function') {
return action(dispatch, getState, extraArguments);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
export default thunk;
配置中间件
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './reducer'
const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware))
// The store now has the ability to accept thunk functions in `dispatch`
const store = createStore(rootReducer, composedEnhancer)
export default store
使用redux-thunk创建异步action
export const fetchWeatherStarted = () => ({
type: FETCH_STARTED
});
export const fetchWeatherSuccess = (result) => ({
type: FETCH_SUCCESS,
result
});
export const fetchWeatherFailure = (error) => ({
type: FETCH_FAILURE,
error
});
export const fetchWeather = (cityCode) => {
return (dispatch) => {
const apiUrl = `/data/cityinfo/${cityCode}.html`;
dispatch(fetchWeatherStarted());
fetch(apiUrl).then((response)=>{
if(response.status !== 200) {
throw new Error('Fail to get response with status ' + response.status);
}
response.json().then((responseJson) => {
dispatch(fetchWeatherSuccess(responseJson.weatherinfo));
}).catch((error)=>{
throw new Error('Invalid json response: ' + error);
});
}).catch(error) => {
dispatch(fetchWeatherFailure(error));
});
};
}
- 使用Promise中间件
实现方式一
function isPromise(obj){
return obj && typeof obj.then === 'function';
}
export default function promiseMiddleware({dispatch}) {
return function(next) {
return function(action){
return isPromise(action)? action.then(dispatch) : next(action);
}
}
}
实现方式二
/**
* action 对象类似下面的格式
* {
* promise: fetch(apiUrl),
* types: ['pending', 'success', 'failure']
* }
*/
export default function promiseMiddleware({dispatch}) {
return (next) => (action) => {
const {types, promise, ...rest} = action;
if(!isPromise(promise) || !(action.types && action.types.length === 3)){
return next(action);
}
const {PENDING, DONE, FAIL] = types;
dispatch({...rest, type: PENDING});
return action.promise.then(
(result) => dispatch({...rest, result, type: DONE}),
(error) => dispatch({...rest, error, type: FAIL}),
);
};
}

浙公网安备 33010602011771号