redux middleWare 原理及异步实战

发布于 2023-01-31 21:35:29 字数 8149 浏览 100 评论 0

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 store 上的dispath 方法。因为中间件可能是异步的,所以这应该是定义在组合链中存储增强器。

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) =&gt; dispatch(...args) } const chain = middlewares.map(middleware =&gt; middleware(middlewareAPI))//将middleware放入链式数组 dispatch = compose(...chain)(store.dispatch)//依次执行 <span class="hljs-built_in">return</span> {// 将增强过的dispatch返回 ...store, dispatch }
}
}

applyMiddleware 的使用方法,官方文档中给出 两种 用法:

const store = createStore(reducer, preloadedState, applyMiddleware(...))
const store = createStore(reducer, 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 函数嵌套调用方法,也就是:

const store = applyMiddleware(...)(createStore)(reducer,preloadedState)

嵌套函数分别传入的 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] ,从右到左依次执行。

dispatch = f1(f2(f3(store.dispatch))))

如图所示,从右至左执行 logger2、logger1。logger2 返回的代码作为参数传给 logger1 的 next 参数。按照图上 1->2(执行下一个 middleware)->3->4(触发 redux 原生的 dispatch 方法)->5->6 完成

五、异步操作

很多时候,我们需要异步操作。用户触发第一个 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 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-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 也可以解决异步的问题。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

轻许诺言

暂无简介

文章
评论
28 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文