zoukankan      html  css  js  c++  java
  • redux-applyMiddleware实现理解+自定义中间件

    前言:

      终于好好理解了middleware。。。。

    1.redux middleware提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。

      redux通过store.dispatch(atcion),发起一个action给store,store接收后把当前state与action一起传给reducer,所以middleware做文章的地方就是dispatch,在原生的dispatch执行之前,先进行一些列的操作。

    实现middleware的方法:下面以logger中间件为例

    1.手动记录(不可能会使用这种的吧)

    let action = addTodo('Use Redux')
    
    console.log('dispatching', action)
    store.dispatch(action)
    console.log('next state', store.getState())

    2.封装dispatch

      要发起action的时候,不用 dispatch 而是用 disptchAddLog()。(把这个方法放在store里面、好像还不错的样子)

    function dispatchAndLog(store, action) {
      console.log('dispatching', action)
      store.dispatch(action)
      console.log('next state', store.getState())
    }

    3.猴子补丁

      通过重写store.dispatch(), (重写之后,所使用的dispatch(), 已经不是原生的dispatch了,使用多个中间件,就是不断改写前一次生成的dispatch),

      (注意:原生的dispatch,已经不可能找到,也就是不能单独使用原始dispatch了)

    function(store) {
        let next = store.dispatch
        store.dispatch = function dispatchAndLog(action) {
            console.log('dispatching', action)
            let result = next(action)
            console.log('next state', store.getState())
            return result
        }
    }

    4.隐藏猴子补丁,

      乍一看好像和猴子补丁没什么却别,但其实它把赋值的给store.dispatch的逻辑放到了中间件函数的外面,需要外面提过一个applyMiddleware辅助函数来完成插值,除此之外,真的没什么区别。(原理还是猴子补丁)

    function logger(store) {
      let next = store.dispatch
    
      // 我们之前的做法:
      // store.dispatch = function dispatchAndLog(action) {
    
      return function dispatchAndLog(action) {
        console.log('dispatching', action)
        let result = next(action)
        console.log('next state', store.getState())
        return result
      }
    }

      多个中间件 的实现方式:

    function applyMiddlewareByMonkeypatching(store, middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
    
      // 在每一个 middleware 中变换 dispatch 方法。
      middlewares.forEach(middleware =>
        store.dispatch = middleware(store)
      )
    }

    5.移除猴子补丁

      相对隐藏猴子不同,把middleware函数里面 let next = store.dispatch ,放到函数外面 dispatch = middleware(store)(dispatch) 

    function logger(store) {
      return function wrapDispatchToAddLogging(next) {
        return function dispatchAndLog(action) {
          console.log('dispatching', action)
          let result = next(action)
          console.log('next state', store.getState())
          return result
        }
      }
    }

      看一下多个中间件实现方式你就知道了,

      是否发现:在 return 的 assign 之前, store.dispatch 都是原生的,并没有被改变。所以中间件里面是否可以使用store.dispatch调用原生的,(如果有需要的话),然而笔者试着调用了一下,并不行,会不断的触发,就是已经是该改变之后的了。

    // 警告:这只是一种“单纯”的实现方式!
    // 这 *并不是* Redux 的 API.
    
    function applyMiddleware(store, middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
    
      let dispatch = store.dispatch
      middlewares.forEach(middleware =>
        dispatch = middleware(store)(dispatch)
      )
    
      return Object.assign({}, store, { dispatch })
    }

      logger中间件换成es6的箭头函数更好看

    const logger = store => next => action => {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }

    6.redux中是现实方法

    这与 Redux 中 applyMiddleware() 的实现已经很接近了,但是有三个重要的不同之处

    • 它只暴露一个 store API 的子集给 middleware:dispatch(action) 和 getState()

    • 它用了一个非常巧妙的方式来保证你的 middleware 调用的是 store.dispatch(action) 而不是 next(action),从而使这个 action 会在包括当前 middleware 在内的整个 middleware 链中被正确的传递。这对异步的 middleware 非常有用。

    • 为了保证你只能应用 middleware 一次,它作用在 createStore() 上而不是 store 本身。因此它的签名不是 (store, middlewares=> store, 而是 (...middlewares=(createStore=> createStore

    总结:

      redux提供了applyMiddleware(), 如果我们自定义中间件,也就变的很简单了

    function myMiddleware = (store) => (next) => (action) => {
        console.log('进入了我自己定义的中间件');
        let result = next(action);    // 执行下一步
        // let result = store.dispatch(action); //不调用next,直接使用store.dispatch调用原生。然后是不行的,会陷入dispatch死循环。
        return result;
    }

      applyMiddleware会帮我们执行前面两层函数。myMiddleware(store)(dispatch)

    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;
  • 相关阅读:
    C#枚举中使用Flags特性
    WPF 设置输入只能英文
    PHP array_merge_recursive() 函数
    PHP array_merge() 函数
    PHP array_map() 函数
    PHP array_keys() 函数
    PHP array_key_exists() 函数
    [HNOI2016]网络
    WPF 设置输入只能英文
    WPF 设置输入只能英文
  • 原文地址:https://www.cnblogs.com/miaowwwww/p/6265323.html
Copyright © 2011-2022 走看看