如果要增强dispatch,我们可以对其进行重构,直接改写 store 实例中的 dispatch:
let store = createStore(rootReducer);
let dispatch = store.dispatch//拿到dispatch
store.dispatch = function (action) {//对dispatch进行重构
console.log('旧状态',store.getState())//1.打印就状态
dispatch(action)//2.在执行之前的action
console.log('新状态',store.getState())//3.打印新状态
}
复制代码
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
源码很精简,理解有点复杂。其实是使用reduce不断将最右先调动函数的结果返回给后调用的函数。
举个栗子,
function add1(str){
return str+1;
}
function add2(str){
return str+2;
}
function add3(str){
return str+3;
}
let add = compose(add3,add2,add1);
let r = add("啊哈哈哈")//啊哈哈哈123
复制代码
Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
Redux解决了react中出现的多交互、多数据源问题,但是如果有异步操作,或者要对操作进行拦截或者执行后回调就比较麻烦。于是我们需要Redux 中间件。
一、手动增强store.dispatch
我们知道 react-redux的 connect是一个高阶组件,它将组件包装之后拿到一个能够在组件中直接获取 context 的 state 的组件,并且用dispatch监听每一个action:
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { ... let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} // 用来 dispatch 的时候获取 store 的 dispatch ... render() { return <WrappedComponent {...this.state.allProps}/> } } return Connect; } 复制代码
如果要增强dispatch,我们可以对其进行重构,直接改写 store 实例中的 dispatch:
let store = createStore(rootReducer); let dispatch = store.dispatch//拿到dispatch store.dispatch = function (action) {//对dispatch进行重构 console.log('旧状态',store.getState())//1.打印就状态 dispatch(action)//2.在执行之前的action console.log('新状态',store.getState())//3.打印新状态 } 复制代码
以上的代码增强了dispatch方法,使执行顺序变成了
action->log->reducer->log
,执行结果如下:二、使用redux的applyMiddleware方法增强store.dispatch
redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截
action -> reducer
的过程。变为action -> middlewares -> reducer
。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。redux applyMiddleware方法源码
export default function applyMiddleware(...middlewares) {//[middleware1,middleware2] return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI))//将middleware放入链式数组 dispatch = compose(...chain)(store.dispatch)//依次执行 return {// 将增强过的dispatch返回 ...store, dispatch } } } 复制代码
applyMiddleware的使用方法,官方文档中给出
两种
用法:第二个参数初始化state不是必传的,源码中的createStore方法对参数进行了处理
三、middleware如何工作
redux createStore源码
export default function createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState) } ...... } 复制代码
在createStore方法中判断参数并相应替换,最后createStore代码执行返回的是一个enhancer函数嵌套调用方法,也就是:
如图所示:嵌套函数分别传入的
createStore和reducer,创建了store
,并定义了dispatch方法,并组合成obj传给了logger函数
logger中间件函数接受两个参数
dispatch getState
(获取状态 派发动作) 并返回一个新的参数next
,形成了一个dispatch增强函数。小白我对于这个一长串的return理解成如下:let logger1 = function({dispatch,getState}) { store.dispatch = function(action){ console.log('旧状态1',getState()) next(action) console.log('新状态1',getState()) } } 复制代码
这已经跟文章开头手动增强store.dispatch的函数十分相近了,主要区别在于next方法。
middleware
通过 next(action) 一层一层处理和传递 action
直到 redux 原生的 dispatch
,这时next为客户端调用的dispatch方法,action为方法传入的actionType:{type:xxx,payload:xxx} 咳,代码要优雅,小白我理解了就要按照官方的来,正确的middleWare一样定义格式如下:let logger = ({dispatch,getState}) => next => action =>{ console.log('旧状态1',getState()) next(action)//dispatch(action) console.log('新状态1',getState()) } 复制代码
中间件的实现和洋葱模型很像,先触发logger第一层,再触发dispatch事件,最后再从logger函数出来。
四、compose实现链式调用
实现多个中间件先后调用的关键是compose函数
redux compose源码链接
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } 复制代码
源码很精简,理解有点复杂。其实是使用reduce不断将
最右
先调动函数的结果返回给后调用的函数。举个栗子,
function add1(str){ return str+1; } function add2(str){ return str+2; } function add3(str){ return str+3; } let add = compose(add3,add2,add1); let r = add("啊哈哈哈")//啊哈哈哈123 复制代码
在这段代码中,compose函数执行顺序为add1->add2->add3,并将
结果作为参数
传给下一个函数。在redux中当新 dispatch 执行时,[f1, f2, ... , fx, ..., fn],从右到左依次执行。
如图所示,从右至左执行logger2,logger1。logger2返回的代码作为参数传给logger1的next参数。按照图上1->2(执行下一个middleware)->3->4(触发redux 原生的 dispatch方法)->5->6 完成
链式middleware流程图如下
五、异步操作
很多时候,我们需要异步操作。用户触发第一个dispatch事件的action,需要发送第二个action。或者根据返回的根据发送第二个处理请求。
解决异步操作的方法:
(1)redux函数的参数是dispatch和getState,把返回的obj改成返回一个异步函数。
(2)异步操作结束之后,再发出一个 Action。
function incrementAsync() { return dispatch => { setTimeout(() => { // Yay! Can invoke sync or async actions with `dispatch` dispatch(increment()); }, 1000); }; } 复制代码
这样子能理想得解决异步操作,而store.dispatch方法正常情况下,参数只能是对象,不能是函数。
这个时候可以引入redux-thunk
redux-thunk实现源码:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk; 复制代码
redux-thunk 做的事情就是判断 action 类型是否是函数,若是,则执行 action,若不是,则继续传递 action 到下个 middleware。
运用方法:
let store = createStore( rootReducer, applyMiddleware(thunk,logger1,logger2) ) 复制代码
如图所示,当store.dispatch运行到thunk中间件,发现返回的是一个function,
则执行返回的函数,并返回,重新派发dispatch
。因此使用redux-thunk,改造store.dispatch。可以实现异步方法
还有如 redux-promise redux-saga也可以解决异步的问题