精华内容
下载资源
问答
  • 自己写redux中间件
    2022-05-31 11:10:16

    1、为什么需要中间件

    默认情况下,redux只能处理同步数据流。但是实际项目开发中,状态的更新、获取、通常是使用异步操作来实现的。

    2、redux中间件的作用:处理具有副作用的功能,比如,异步操作就是常见的side effect

    3、中间件的理解:

    中间件,可以理解为处理一个功能的中间环节。

    Redux中间件用来处理状态更新,也就是在状态更新的过程中,执行一系列操作。

    一、redux-logger中间件(如果使用了中间件,logger应该放在最后)

    1、安装yarn add redux-logger

    2、导入redux-logger中间件

    3、从redux中导入applyMiddleware函数

    4、调用applyMiddleware()并传入logger中间件作为参数

    5、将applyMiddleware()调用作为createStore函数的第二个参数

    import { createStore,applyMiddleware } from "redux";
    import logger from 'redux-logger'
    import reducer from './reducers'
    
    const store=createStore(reducer,applyMiddleware(logger))
    
    export default store

    二、redux-thunk中间件

    1、用来可以处理函数形式的action。因此,在函数形式的action就可以执行异步操作。

    2、thunk action是一个函数。函数包含两个参数dispatch getState

    const thunkAction=()=>{
        return (dispatch,getState)=>{
            setTimeout(() => {
                // 执行异步操作
            }, 500);
        }
    }

    使用

    1、yarn add redux-thunk

    2、导入redux-thunk

    3、将thunk添加到applyMiddleware函数的参数中

    4、创建函数形式的action,在函数中执行异步操作

    import { createStore,applyMiddleware } from "redux";
    import logger from 'redux-logger'
    import thunk from "redux-thunk";
    import reducer from './reducers'
    
    const store=createStore(reducer,applyMiddleware(thunk,logger))
    
    export default store
    export const addTodo=(name)=>{
        return (dispatch)=>{
            setTimeout(() => {
                dispatch({type:types.ADD_TODO,payload:{
                    id:Date.now(),
                    name,
                    done:false
                }})
            }, 500);
        }
    }

    三、redux-devtools-extension中间件(有这个就可以不用loggle了)

    目标:使用chrome开发者工具调试跟踪redux状态

    步骤:

    1、yarn add redux-devtools-extension

    2、从该中间件中导入composeWithDevTools函数

    3、调试该函数,将applyMiddleware作为参数传入

    4、打开Chrome浏览器的redux开发者工具并使用

    import { createStore,applyMiddleware } from "redux";
    import { composeWithDevTools } from "redux-devtools-extension";
    import logger from 'redux-logger'
    import thunk from "redux-thunk";
    import reducer from './reducers'
    
    const store=createStore(reducer,composeWithDevTools(applyMiddleware(thunk,logger)))
    
    export default store

    更多相关内容
  • Redux 中间件

    2022-04-29 10:45:11
    Redux 中间件作用: 中间件说明: 2.中间件的触发时机 3.redux-logger中间件 4.redux-thunk中间件 1.不使用 redux-thunk 中间件,action 只能是一个对象 2.使用 redux-thunk 中间件后,action 既可以是对象,...

    目录

    1.中间件概述

    Redux 中间件作用:

    中间件说明:

    2.中间件的触发时机

    3.redux-logger中间件 

    4.redux-thunk中间件 

    1.不使用 redux-thunk 中间件,action 只能是一个对象

    2.使用 redux-thunk 中间件后,action 既可以是对象,又可以是函数

    5.了解:redux-thunk中间件原理

    6.redux-devtools-extension中间件

    7.了解:redux 中间件原理

    8.理解Redux异步数据流

    1.中间件概述

    目标: 能够理解为什么需要redux中间件

    内容:

    默认情况下,Redux 自身只能处理同步数据流。但是在实际项目开发中,状态的更新、获取,通常是使用异步操作来实现。

    • 问题:如何在 Redux 中进行异步操作呢?
    • 回答:通过 Redux 中间件机制来实现

    Redux 中间件作用:

    处理具有副作用(side effect)的功能,比如,异步操作就是最常见的 side effect

    中间件说明:

    • 中间件,可以理解为处理一个功能的中间环节
    • 中间件的优势:可以串联、组合,在一个项目中使用多个中间件
    • Redux 中间件用来处理 状态 更新,也就是在 状态 更新的过程中,执行一系列的相应操作

    2.中间件的触发时机

    目标:能够理解中间件的触发时机

    内容

    • Redux 中间件执行时机:在 dispatching action 和 到达 reducer 之间
    • 没有中间件:dispatch(action) => reducer

    • 使用中间件:dispatch(action) => 执行中间件代码 => reducer

    • 原理:封装了 redux 自己的 dispatch 方法
      • 没有中间件:store.dispatch() 就是 Redux 库自己提供的 dispatch 方法,用来发起状态更新
      • 使用中间件:store.dispatch() 就是中间件封装处理后的 dispatch,但是,最终一定会调用 Redux 自己的 dispatch 方法发起状态更新

    3.redux-logger中间件 

    目标:能够使用redux-logger中间件记录日志

    步骤

    1. 安装:yarn add redux-logger
    2. 导入 redux-logger 中间件
    3. 从 redux 中导入 applyMiddleware 函数
    4. 调用 applyMiddleware() 并传入 logger 中间件作为参数
    5. 将 applyMiddleware() 调用作为 createStore 函数的第二个参数

    然后,调用 store.dispatch() 查看 console 中 logger 中间件记录的日志信息

    核心代码

    store/index.js 中:

    import { createStore, applyMiddleware } from 'redux'
    import logger from 'redux-logger'
    import rootReducer from './reducers'
    
    const store = createStore(rootReducer, applyMiddleware(logger))

    4.redux-thunk中间件 

    目标:能够使用redux-thunk中间件处理异步操作

    内容

    redux-thunk 中间件可以处理函数形式的 action。因此,在函数形式的 action 中就可以执行异步操作

    语法:

    • thunk action 是一个函数
    • 函数包含两个参数:1 dispatch 2 getState
    // 函数形式的 action
    const thunkAction = () => {
      return (dispatch, getState) => {}
    }
    
    // 解释:
    const thunkAction = () => {
      // 注意:此处返回的是一个函数,返回的函数有两个参数:
    	// 第一个参数:dispatch 函数,用来分发 action
      // 第二个参数:getState 函数,用来获取 redux 状态
      return (dispatch, getState) => {
        setTimeout(() => {
          // 执行异步操作
          // 在异步操作成功后,可以继续分发对象形式的 action 来更新状态
        }, 1000)
      }
    }

    使用 redux-thunk 中间件前后对比:

    1.不使用 redux-thunk 中间件,action 只能是一个对象

    // 1 普通 action 对象
    { type: 'counter/increment' }
    dispatch({ type: 'counter/increment' })
    
    // 2 action creator
    const increment = payload => ({ type: 'counter/increment', payload })
    dispatch(increment(2))

    2.使用 redux-thunk 中间件后,action 既可以是对象,又可以是函数

    // 1 对象:
    // 使用 action creator 返回对象
    const increment = payload => ({ type: 'counter/increment', payload })
    // 分发同步 action
    dispatch(increment(2))
    
    // 2 函数:
    // 使用 action creator 返回函数
    const incrementAsync = () => {
      return (dispatch, getState) => {
        // ... 执行异步操作代码
      }
    }
    // 分发异步 action
    dispatch(incrementAsync())

    步骤

    1. 安装:yarn add redux-thunk
    2. 导入 redux-thunk
    3. 将 thunk 添加到 applyMiddleware 函数的参数(中间件列表)中
    4. 创建函数形式的 action,在函数中执行异步操作

    核心代码

    store/index.js 中:

    // 导入 thunk 中间件
    import thunk from 'redux-thunk'
    // 将 thunk 添加到中间件列表中
    // 知道:如果中间件中使用 logger 中间件,logger 中间件应该出现在 applyMiddleware 的最后一个参数
    const store = createStore(rootReducer, applyMiddleware(thunk, logger))

     actions/index.js 中:

    export const clearAllAsync = () => {
      return (dispatch) => {
        // 处理异步的代码:1 秒后再清理已完成任务
        setTimeout(() => {
          dispatch(clearAll())
        }, 1000)
      }
    }

     App.js 中:

    import { clearTodoAsync } from '../store/actions/todos'
    
    const TodoFooter = () => {
    	return (
        // ...
      	<button
          className="clear-completed"
          onClick={() => dispatch(clearTodoAsync())}
        >
          Clear completed
        </button>
      )
    }

    5.了解:redux-thunk中间件原理

    目标:能够了解redux-thunk中间件的原理

    内容

    function createThunkMiddleware(extraArgument) {
      // Redux 中间件的写法:const myMiddleware = store => next => action => { /* 此处写 中间件 的代码 */ }
      return ({ dispatch, getState }) => (next) => (action) => {
        // redux-thunk 的核心代码:
        // 判断 action 的类型是不是函数
        
        // 如果是函数,就调用该函数(action),并且传入了 dispatch 和 getState
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
        
        // 如果不是函数,就调用下一个中间件(next),将 action 传递过去
        // 如果没有其他中间件,那么,此处的 next 指的就是:Redux 自己的 dispatch 方法
        return next(action);
      };
    }
    
    // 所以,在使用了 redux-thunk 中间件以后,那么,redux 就既可以处理 对象形式的 action 又可以处理 函数形式的 action 了
    // 1 处理对象形式的 action
    dispatch({ type: 'todos/clearAll' }) // 对应上面第 14 行代码
    
    // 2 处理函数型的 action
    export const clearAllAsync = () => {
      return dispatch => {
        // 在此处,执行异步操作
        setTimeout(() => {
          // 异步操作完成后,如果想要修改 redux 中的状态,就必须要
          // 分发一个 对象形式的 action(同步的 action)
          dispatch({ type: types.CLEAR_ALL })
        }, 1000)
      }
    }
    dispatch(clearAllAsync()) // 对应上面第 8、9 行代码

    6.redux-devtools-extension中间件

    目标:能够使用chrome开发者工具调试跟踪redux状态

    内容

    步骤

    1. 安装: yarn add redux-devtools-extension
    2. 从该中间件中导入 composeWithDevTools 函数
    3. 调用该函数,将 applyMiddleware() 作为参数传入
    4. 打开 Chrome 浏览器的 redux 开发者工具并使用
    import thunk from 'redux-thunk'
    import { composeWithDevTools } from 'redux-devtools-extension'
    
    const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))
    
    export default store

    7.了解:redux 中间件原理

    • Redux 中间件原理:创建一个函数,包装 store.dispatch,使用新创建的函数作为新的 dispatch

    • 比如下图,logger 就是一个中间件,使用该中间件后 store.dispatch 就是包装后的新 dispatch

    • 中间件修改了 store.dispatch,在分发动作和到达 reducer 之间提供了扩展

    • redux 中间件采用了 洋葱模型 来实现

    • 自己实现记录日志的 redux 中间件:

    // 简化写法:
    // store 表示:redux 的 store
    // next 表示:下一个中间件,如果只使用一个中间,那么 next 就是 store.dispatch(redux 自己的 dispatch 函数)
    // action 表示:要分发的动作
    const logger = store => next => action => {
      console.log('prev state:', store.getState()) // 更新前的状态
      // 记录日志代码
      console.log('dispatching', action)
      // 如果只使用了一个中间件:
      // 那么,next 就表示原始的 dispatch
      // 也就是:logger中间件包装了 store.dispatch
      let result = next(action)
      // 上面 next 代码执行后,redux 状态就已经更新了,所以,再 getState() 拿到的就是更新后的最新状态值
      // 记录日志代码
      console.log('next state', store.getState()) // 更新后的状态
      return result
    }
    
    // 完整写法:
    const logger = store => {
      return next => {
        return action => {
          // 中间件代码写在这个位置:
        }
      }
    }

    8.理解Redux异步数据流

    目标:能够说出redux的异步数据流动过程

    内容

    展开全文
  • Redux中间件,用于处理与和完全兼容的全局加载 用法 Redux加载中间件为每个未解决的返回承诺的动作设置了加载状态,在承诺解析后,它将其加载状态设置为false。 此外,它还链接了在200ms阈值内运行的未解决的动作...
  • redux-middleware-workers v1.0.1推荐技术安装套件npm i -save redux-middleware-workers yarn add redux-middleware-workers 例子// middleware.js// worker loader is a webpack package that loads the worker ...
  • redux中间件详解

    千次阅读 2021-07-30 11:33:13
    1、redux中间件简介 1.1、什么是redux中间件 redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> ...

    1、redux中间件简介

    在这里插入图片描述

    1.1、什么是redux中间件

    redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。

    通俗来说,redux中间件就是对dispatch的功能做了扩展。

    先来看一下传统的redux执行流程:

    请添加图片描述

    图1 redux传统执行流程

    代码示例:

    import { createStore } from 'redux';
    
    /**
     * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
     * 描述了 action 如何把 state 转变成下一个 state。
     */
    function counter(state = 0, action) {
      switch (action.type) {
      case 'INCREMENT':
        return state + 1;
      case 'DECREMENT':
        return state - 1;
      default:
        return state;
      }
    }
    
    // 创建 Redux store 来存放应用的状态。
    // API 是 { subscribe, dispatch, getState }。
    let store = createStore(counter);
    
    // 可以手动订阅更新,也可以事件绑定到视图层。
    store.subscribe(() =>
      console.log(store.getState())
    );
    
    // 改变内部 state 惟一方法是 dispatch 一个 action。
    // action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
    store.dispatch({ type: 'INCREMENT' });
    // 1
    store.dispatch({ type: 'INCREMENT' });
    // 2
    store.dispatch({ type: 'DECREMENT' });
    // 1
    

    Redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API。

    对dispatch改造后,效果如下:

    在这里插入图片描述

    图2 dispatch改造后的执行流程

    如上图所示,dispatch派发给 redux Store 的 action 对象,到达reducer之前,进行一些额外的操作,会被 Store 上的多个中间件依次处理。例如可以利用中间件来进行日志记录、创建崩溃报告、调用异步接口或者路由等等,那么其实所有的对 action 的处理都可以有中间件组成的。
    简单来说,中间件就是对store.dispatch()的增强。

    1.2、使用redux中间件

    redux有很多中间件,我们这里以 redux-thunk 为例。

    代码示例:

    import { applyMiddleware, createStore } from 'redux';
    import thunk from 'redux-thunk';
     const store = createStore(
      reducers, 
      applyMiddleware(thunk)
    );
    

    直接将thunk中间件引入,放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。即可以在reducer中进行一些异步的操作。

    Redux middleware 提供了一个分类处理 action 的机会。在 middleware 中,我们可以检阅每一个流过的 action,并挑选出特定类型的 action 进行相应操作,以此来改变 action。其实applyMiddleware就是Redux的一个原生方法,将所有中间件组成一个数组,依次执行。
    中间件多了可以当做参数依次传进去。

    代码示例:

    import { applyMiddleware, createStore } from 'redux';
    import thunk from 'redux-thunk';
    import createLogger from 'redux-logger';
    
    const logger = createLogger();
    
    const store = createStore(
      reducers, 
      applyMiddleware(thunk, logger) //会按顺序执行
    );
    

    2、中间件的运行机制

    2.1、createStore源码分析

    源码:

    // 摘至createStore
    export function createStore(reducer, rootState, enhance) {
        //...
        
        if (typeof enhancer !== 'undefined') {
            if (typeof enhancer !== 'function') {
              throw new Error('Expected the enhancer to be a function.')
            }
           /*
            若使用中间件,这里 enhancer 即为 applyMiddleware()
            若有enhance,直接返回一个增强的createStore方法,可以类比成react的高阶函数
           */
           return enhancer(createStore)(reducer, preloadedState)
      }
        
      //...
    }
    

    对于createStore的源码我们只需要关注和applyMiddleware有关的地方, 通过源码得知在调用createStore时传入的参数进行一个判断,并对参数做矫正。 据此可以得出createStore有多种使用方法,根据第一段参数判断规则,我们可以得出createStore的两种使用方式:

    const store = createStore(reducer, {a: 1, b: 2}, applyMiddleware(...));
    

    或:

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

    经过createStore中的第一个参数判断规则后,对参数进行了校正,得到了新的enhancer得值,如果新的enhancer的值不为undeifined,便将createStore传入enhancer(即applyMiddleware调用后返回的函数)内,让enhancer执行创建store的过程。也就时说这里的:

    enhancer(createStore)(reducer, preloadedState);
    

    实际上等同于:

    applyMiddleware(mdw1, mdw2, mdw3)(createStore)(reducer, preloadedState);
    

    applyMiddleware会有两层柯里化,同时表明它还有一种很函数式编程的用法,即 :

    const store = applyMiddleware(mdw1, mdw2, mdw3)(createStore);
    

    这种方式将创建store的步骤完全放在了applyMiddleware内部,并在其内第二层柯里化的函数内执行创建store的过程即调用createStore,调用后程序将跳转至createStore走参数判断流程最后再创建store。

    无论哪一种执行createStore的方式,我们都终将得到store,也就是在creaeStore内部最后返回的那个包含dispatch、subscribe、getState等方法的对象。

    2.2、applyMiddleware源码分析

    源码:

    export default function applyMiddleware(...middlewares) {  return createStore => (...args) => {    // 利用传入的createStore和reducer和创建一个store    const store = createStore(...args)    let dispatch = () => {      throw new Error(      )    }    const middlewareAPI = {      getState: store.getState,      dispatch: (...args) => dispatch(...args)    }    // 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍    const chain = middlewares.map(middleware => middleware(middlewareAPI))    // 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch    dispatch = compose(...chain)(store.dispatch)    return {      ...store,      dispatch    }  }}
    

    为方便阅读和理解,部分ES6箭头函数已修改为ES5的普通函数形式,如下:

    function applyMiddleware (...middlewares){    return function (createStore){        return function (reducer, preloadedState, enhancer){            const store = createStore(reducer, preloadedState, enhancer);            let dispatch = function (){                throw new Error()            };            const middlewareAPI = {                getState: store.getState,                dispatch: (...args) => dispatch(...args)            };            //一下两行代码是所有中间件被串联起来的核心部分实现			// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍            const chain = middlewares.map(middleware => middleware(middlewareAPI));			// 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch            dispatch = compose(...chain)(store.dispatch);            return {                ...store,                dispatch            };        }    }}
    

    从上面的代码我们不难看出,applyMiddleware 这个函数的核心就在于在于组合 compose,通过将不同的 middlewares 一层一层包裹到原生的 dispatch 之上,然后对 middleware 的设计采用柯里化的方式,以便于compose ,从而可以动态产生 next 方法以及保持 store 的一致性。

    在函数式编程(Functional Programming)相关的文章中,经常能看到 柯里化(Currying)这个名词。它是数学家柯里(Haskell Curry)提出的。

    柯里化,用一句话解释就是,把一个多参数的函数转化为单参数函数的方法。

    根据源码,我们可以将其主要功能按步骤划分如下:

    1、依次执行middleware

    middleware执行后返回的函数合并到一个chain数组,这里我们有必要看看标准middleware的定义格式,如下:

    const chain = middlewares.map(middleware => middleware(middlewareAPI));
    

    遍历所有的中间件,并调用它们,传入那个类似于store的对象middlewareAPI,这会导致中间件中第一层柯里化函数被调用,并返回一个接收next(即dispatch)方法作为参数的新函数。

    export default store => next => action => {}// 即function (store) {    return function(next) {        return function (action) {            return {}        }    }}
    

    那么此时合并的chain结构如下:

    [    ...,    function(next) {        return function (action) {            return {}        }    }]
    

    2、改变dispatch指向

    dispatch = compose(...chain)(store.dispatch);
    

    我们展开了这个数组,并将其内部的元素(函数)传给了compose函数,compose函数又返回了我们一个新函数。然后我们再调用这个新函数并传入了原始的未经任何修改的dispatch方法,最后返回一个经过了修改的新的dispatch方法。

    什么是compose?在函数式编程中,compose指接收多个函数作为参数,并返回一个新的函数的方式。调用新函数后传入一个初始的值作为参数,该参数经最后一个函数调用,将结果返回并作为倒数第二个函数的入参,倒数第二个函数调用完后,将其结果返回并作为倒数第三个函数的入参,依次调用,知道最后调用完传入compose的所有的函数后,返回一个最后的结果。

    compose函数如下:
    [...chain].reduce((a, b) => (...args) => a(b(...args)))
    实际就是一个柯里化函数,即将所有的middleware合并成一个middleware,并在最后一个middleware中传入当前的dispatch

    // 假设chain如下:chain = [    a: next => action => { console.log('第1层中间件') return next(action) }    b: next => action => { console.log('第2层中间件') return next(action) }    c: next => action => { console.log('根dispatch') return next(action) }]
    

    调用compose(...chain)(store.dispatch)后返回a(b(c(dispatch)))
    可以发现已经将所有middleware串联起来了,并同时修改了dispatch的指向。
    最后看一下这时候compose执行返回,如下:

    dispatch = a(b(c(dispatch)))// 调用dispatch(action)// 执行循序/*   1. 调用 a(b(c(dispatch)))(action) __print__: 第1层中间件   2. 返回 a: next(action) 即b(c(dispatch))(action)   3. 调用 b(c(dispatch))(action) __print__: 第2层中间件   4. 返回 b: next(action) 即c(dispatch)(action)   5. 调用 c(dispatch)(action) __print__: 根dispatch   6. 返回 c: next(action) 即dispatch(action)   7. 调用 dispatch(action)*/
    

    总结来说就是:

    在中间件串联的时候,middleware1-3的串联顺序是从右至左的,也就是middleware3被包裹在了最里面,它内部含有对原始的store.dispatch的调用,middleware1被包裹在了最外边。

    当我们在业务代码中dispatch一个action时,也就是中间件执行的时候,middleware1-3的执行顺序是从左至右的,因为最后被包裹的中间件,将被最先执行。

    如图所示:

    请添加图片描述

    3、常见的redux中间件

    3.1、logger日志中间件

    源码:

    function createLogger(options = {}) {  /**   * 传入 applyMiddleWare 的函数   * @param  {Function} { getState      }) [description]   * @return {[type]}      [description]   */  return ({ getState }) => (next) => (action) => {    let returnedValue;    const logEntry = {};    logEntry.prevState = stateTransformer(getState());    logEntry.action = action;    // ....     returnedValue = next(action);    // ....    logEntry.nextState = stateTransformer(getState());    // ....    return returnedValue;  };}export default createLogger;
    

    为了方便查看,将代码修改为ES5之后,如下:

    /** * getState 可以返回最新的应用 store 数据 */function ({getState}) {    /**     * next 表示执行后续的中间件,中间件有可能有多个     */    return function (next) {        /**         * 中间件处理函数,参数为当前执行的 action          */        return function (action) {...}    }}
    

    这样的结构本质上就是为了将 middleware 串联起来执行。

    3.2、redux异步管理中间件

    在多种中间件中,处理 redux 异步事件的中间件,绝对占有举足轻重的地位。从简单的 react-thunk 到 redux-promise 再到 redux-saga等等,都代表这各自解决redux异步流管理问题的方案。

    3.2.1、redux-thunk

    redux-thunk的使用:

    function getWeather(url, params) {    return (dispatch, getState) => {        fetch(url, params)            .then(result => {                dispatch({                    type: 'GET_WEATHER_SUCCESS', payload: result,                });            })            .catch(err => {                dispatch({                    type: 'GET_WEATHER_ERROR', error: err,                });            });        };}
    

    在上述使用实例中,我们应用thunk中间到redux后,可以dispatch一个方法,在方法内部我们想要真正dispatch一个action对象的时候再执行dispatch即可,特别是异步操作时非常方便。

    源码:

    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;
    

    为了方便阅读,源码中的箭头函数在这里换成了普通函数,如下:

    function createThunkMiddleware (extraArgument){    return function ({dispatch, getState}){        return function (next){            return function (action){                if (typeof action === 'function'){                    return action(dispatch, getState, extraArgument);                }                return next(action);            };        }    }}let thunk = createThunkMiddleware();thunk.withExtraArgument = createThunkMiddleware;export default thunk;
    

    thunk是一个很常用的redux中间件,应用它之后,我们可以dispatch一个方法,而不仅限于一个纯的action对象。它的源码也很简单,如上所示,除去语法固定格式也就区区几行。

    下面我们就来看看源码(为了方便阅读,源码中的箭头函数在这里换成了普通函数),首先是这三层柯里化:

    // 外层function createThunkMiddleware (extraArgument){     // 第一层    return function ({dispatch, getState}){       // 第二层        return function (next){            // 第三层            return function (action){                if (typeof action === 'function'){                    return action(dispatch, getState, extraArgument);                }                return next(action);            };        }    }}
    

    首先是外层,上面的源码可知,这一层存在的主要目的是支持在调用applyMiddleware并传入thunk的时候时候可以不直接传入thunk本身,而是先调用包裹了thunk的函数(第一层柯里化的父函数)并传入需要的额外参数,再将该函数调用的后返回的值(也就是真正的thunk)传给applyMiddleware,从而实现对额外参数传入的支持,使用方式如下:

    const store = createStore(reducer, applyMiddleware(thunk.withExtraArgument({api, whatever})));
    

    如果无需额外参数则用法如下:

    const store = createStore(reducer, applyMiddleware(thunk));
    

    接下来来看第一层,这一层是真正applyMiddleware能够调用的一层,从形参来看,这个函数接收了一个类似于store的对象,因为这个对象被结构以后获取了它的dispatch和getState这两个方法,巧的是store也有这两方法,但这个对象到底是不是store,还是只借用了store的这两方法合成的一个新对象?这个问题在我们后面分析applyMiddleware源码时,自会有分晓。

    再来看第二层,在第二层这个函数中,我们接收的一个名为next的参数,并在第三层函数内的最后一行代码中用它去调用了一个action对象,感觉有点 dispatch({type: ‘XX_ACTION’, data: {}}) 的意思,因为我们可以怀疑它就是一个dispatch方法,或者说是其他中间件处理过的dispatch方法,似乎能通过这行代码链接上所有的中间件,并在所有只能中间件自身逻辑处理完成后,最终调用真实的store.dispath去dispatch一个action对象,再走到下一步,也就是reducer内。

    最后我们看看第三层,在这一层函数的内部源码中首先判断了action的类型,如果action是一个方法,我们就调用它,并传入dispatch、getState、extraArgument三个参数,因为在这个方法内部,我们可能需要调用到这些参数,至少dispatch是必须的。**这三行源码才是真正的thunk核心所在。所有中间件的自身功能逻辑也是在这里实现的。**如果action不是一个函数,就走之前解析第二层时提到的步骤。

    3.2.2、redux-promise

    不同的中间件都有着自己的适用场景,react-thunk 比较适合于简单的API请求的场景,而 Promise 则更适合于输入输出操作,比较fetch函数返回的结果就是一个Promise对象,下面就让我们来看下最简单的 Promise 对象是怎么实现的:

    import { isFSA } from 'flux-standard-action';function isPromise(val) {  return val && typeof val.then === 'function';}export default function promiseMiddleware({ dispatch }) {  return next => action => {    if (!isFSA(action)) {      return isPromise(action)        ? action.then(dispatch)        : next(action);    }    return isPromise(action.payload)      ? action.payload.then(          result => dispatch({ ...action, payload: result }),          error => {            dispatch({ ...action, payload: error, error: true });            return Promise.reject(error);          }        )      : next(action);  };}
    

    它的逻辑也很简单主要是下面两部分:

    1. 先判断是不是标准的 flux action。如果不是,那么判断是否是 promise, 是的话就执行 action.then(dispatch),否则执行 next(action)。
    2. 如果是, 就先判断 payload 是否是 promise,如果是的话 payload.then 获取数据,然后把数据作为 payload 重新 dispatch({ …action, payload: result}) ;不是的话就执行 next(action)

    结合 redux-promise 我们就可以利用 es7 的 async 和 await 语法,来简化异步操作了,比如这样:

    const fetchData = (url, params) => fetch(url, params)async function getWeather(url, params) {    const result = await fetchData(url, params)    if (result.error) {        return {            type: 'GET_WEATHER_ERROR', error: result.error,        }    }        return {            type: 'GET_WEATHER_SUCCESS', payload: result,        }    }
    

    3.2.3、redux-saga

    redux-saga是一个管理redux应用异步操作的中间件,用于代替 redux-thunk 的。它通过创建 Sagas 将所有异步操作逻辑存放在一个地方进行集中处理,以此将react中的同步操作与异步操作区分开来,以便于后期的管理与维护。对于Saga,我们可简单定义如下:

    Saga = Worker + Watcher

    redux-saga相当于在Redux原有数据流中多了一层,通过对Action进行监听,从而捕获到监听的Action,然后可以派生一个新的任务对state进行维护(这个看项目本身的需求),通过更改的state驱动View的变更。如下图所示:

    请添加图片描述

    saga特点:

    1. saga 的应用场景是复杂异步。
    2. 可以使用 takeEvery 打印 logger(logger大法好),便于测试。
    3. 提供 takeLatest/takeEvery/throttle 方法,可以便利的实现对事件的仅关注最近实践还是关注每一次实践的时间限频。
    4. 提供 cancel/delay 方法,可以便利的取消或延迟异步请求。
    5. 提供 race(effects),[…effects] 方法来支持竞态和并行场景。
    6. 提供 channel 机制支持外部事件。
    function *getCurrCity(ip) {    const data = yield call('/api/getCurrCity.json', { ip })    yield put({        type: 'GET_CITY_SUCCESS', payload: data,    })}function * getWeather(cityId) {    const data = yield call('/api/getWeatherInfo.json', { cityId })    yield put({        type: 'GET_WEATHER_SUCCESS', payload: data,    })}function loadInitData(ip) {    yield getCurrCity(ip)    yield getWeather(getCityIdWithState(state))    yield put({        type: 'GET_DATA_SUCCESS',    })}
    

    总的来讲Redux Saga适用于对事件操作有细粒度需求的场景,同时它也提供了更好的可测试性,与可维护性,比较适合对异步处理要求高的大型项目,而小而简单的项目完全可以使用redux-thunk就足以满足自身需求了。毕竟react-thunk对于一个项目本身而言,毫无侵入,使用极其简单,只需引入这个中间件就行了。而react-saga则要求较高,难度较大。

    展开全文
  • 1、redux中间件简介 1.1、什么是redux中间件 redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> ...

    1、redux中间件简介

    1.1、什么是redux中间件

    redux 提供了类似后端 Express 的中间件概念,本质的目的是提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。

    通俗来说,redux中间件就是对dispatch的功能做了扩展。

    先来看一下传统的redux执行流程:
    在这里插入图片描述

    图1 redux传统执行流程

    代码示例:
    import { createStore } from 'redux';
    
    /**
     * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
     * 描述了 action 如何把 state 转变成下一个 state。
     */
    function counter(state = 0, action) {
      switch (action.type) {
      case 'INCREMENT':
        return state + 1;
      case 'DECREMENT':
        return state - 1;
      default:
        return state;
      }
    }
    
    // 创建 Redux store 来存放应用的状态。
    // API 是 { subscribe, dispatch, getState }。
    let store = createStore(counter);
    
    // 可以手动订阅更新,也可以事件绑定到视图层。
    store.subscribe(() =>
      console.log(store.getState())
    );
    
    // 改变内部 state 惟一方法是 dispatch 一个 action。
    // action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
    store.dispatch({ type: 'INCREMENT' });
    // 1
    store.dispatch({ type: 'INCREMENT' });
    // 2
    store.dispatch({ type: 'DECREMENT' });
    // 1
    

    Redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API。

    对dispatch改造后,效果如下:

    在这里插入图片描述

    图2 dispatch改造后的执行流程

    如上图所示,dispatch派发给 redux Store 的 action 对象,到达reducer之前,进行一些额外的操作,会被 Store 上的多个中间件依次处理。例如可以利用中间件来进行日志记录、创建崩溃报告、调用异步接口或者路由等等,那么其实所有的对 action 的处理都可以有中间件组成的。 简单来说,中间件就是对store.dispatch()的增强。

    1.2、使用redux中间件

    redux有很多中间件,我们这里以 redux-thunk 为例。

    代码示例:

    import { applyMiddleware, createStore } from 'redux';
    import thunk from 'redux-thunk';
     const store = createStore(
      reducers, 
      applyMiddleware(thunk)
    );
    

    直接将thunk中间件引入,放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。即可以在reducer中进行一些异步的操作。

    Redux middleware 提供了一个分类处理 action 的机会。在 middleware 中,我们可以检阅每一个流过的 action,并挑选出特定类型的 action 进行相应操作,以此来改变 action。其实applyMiddleware就是Redux的一个原生方法,将所有中间件组成一个数组,依次执行。
    中间件多了可以当做参数依次传进去。

    代码示例:

    import { applyMiddleware, createStore } from 'redux';
    import thunk from 'redux-thunk';
    import createLogger from 'redux-logger';
    
    const logger = createLogger();
    
    const store = createStore(
      reducers, 
      applyMiddleware(thunk, logger) //会按顺序执行
    );
    

    2、中间件的运行机制

    2.1、createStore源码分析

    源码:

    // 摘至createStore
    export function createStore(reducer, rootState, enhance) {
        //...
        
        if (typeof enhancer !== 'undefined') {
            if (typeof enhancer !== 'function') {
              throw new Error('Expected the enhancer to be a function.')
            }
           /*
            若使用中间件,这里 enhancer 即为 applyMiddleware()
            若有enhance,直接返回一个增强的createStore方法,可以类比成react的高阶函数
           */
           return enhancer(createStore)(reducer, preloadedState)
      }
        
      //...
    }
    

    对于createStore的源码我们只需要关注和applyMiddleware有关的地方, 通过源码得知在调用createStore时传入的参数进行一个判断,并对参数做矫正。 据此可以得出createStore有多种使用方法,根据第一段参数判断规则,我们可以得出createStore的两种使用方式:

    const store = createStore(reducer, {a: 1, b: 2}, applyMiddleware(...));
    

    或:

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

    经过createStore中的第一个参数判断规则后,对参数进行了校正,得到了新的enhancer得值,如果新的enhancer的值不为undeifined,便将createStore传入enhancer(即applyMiddleware调用后返回的函数)内,让enhancer执行创建store的过程。也就时说这里的:

    enhancer(createStore)(reducer, preloadedState);
    

    实际上等同于:

    applyMiddleware(mdw1, mdw2, mdw3)(createStore)(reducer, preloadedState);
    

    applyMiddleware会有两层柯里化,同时表明它还有一种很函数式编程的用法,即 :

    const store = applyMiddleware(mdw1, mdw2, mdw3)(createStore);
    

    这种方式将创建store的步骤完全放在了applyMiddleware内部,并在其内第二层柯里化的函数内执行创建store的过程即调用createStore,调用后程序将跳转至createStore走参数判断流程最后再创建store。

    无论哪一种执行createStore的方式,我们都终将得到store,也就是在creaeStore内部最后返回的那个包含dispatch、subscribe、getState等方法的对象。

    2.2、applyMiddleware源码分析

    源码:

    export default function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        // 利用传入的createStore和reducer和创建一个store
        const store = createStore(...args)
        let dispatch = () => {
          throw new Error(
          )
        }
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        // 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        // 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
        dispatch = compose(...chain)(store.dispatch)
        return {
          ...store,
          dispatch
        }
      }
    }
    

    为方便阅读和理解,部分ES6箭头函数已修改为ES5的普通函数形式,如下:

    function applyMiddleware (...middlewares){
        return function (createStore){
            return function (reducer, preloadedState, enhancer){
                const store = createStore(reducer, preloadedState, enhancer);
                let dispatch = function (){
                    throw new Error()
                };
    
                const middlewareAPI = {
                    getState: store.getState,
                    dispatch: (...args) => dispatch(...args)
                };
                //一下两行代码是所有中间件被串联起来的核心部分实现
    			// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
                const chain = middlewares.map(middleware => middleware(middlewareAPI));
    			// 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
                dispatch = compose(...chain)(store.dispatch);
    
                return {
                    ...store,
                    dispatch
                };
            }
        }
    }
    

    从上面的代码我们不难看出,applyMiddleware 这个函数的核心就在于在于组合 compose,通过将不同的 middlewares 一层一层包裹到原生的 dispatch 之上,然后对 middleware 的设计采用柯里化的方式,以便于compose ,从而可以动态产生 next 方法以及保持 store 的一致性。

    在函数式编程(Functional Programming)相关的文章中,经常能看到 柯里化(Currying)这个名词。它是数学家柯里(Haskell Curry)提出的。

    柯里化,用一句话解释就是,把一个多参数的函数转化为单参数函数的方法。

    根据源码,我们可以将其主要功能按步骤划分如下:

    1、依次执行middleware

    middleware执行后返回的函数合并到一个chain数组,这里我们有必要看看标准middleware的定义格式,如下:

    const chain = middlewares.map(middleware => middleware(middlewareAPI));
    

    遍历所有的中间件,并调用它们,传入那个类似于store的对象middlewareAPI,这会导致中间件中第一层柯里化函数被调用,并返回一个接收next(即dispatch)方法作为参数的新函数。

    export default store => next => action => {}
    
    // 即
    function (store) {
        return function(next) {
            return function (action) {
                return {}
            }
        }
    }
    

    那么此时合并的chain结构如下:

    [    ...,
        function(next) {
            return function (action) {
                return {}
            }
        }
    ]
    

    2、改变dispatch指向

    dispatch = compose(...chain)(store.dispatch);
    

    我们展开了这个数组,并将其内部的元素(函数)传给了compose函数,compose函数又返回了我们一个新函数。然后我们再调用这个新函数并传入了原始的未经任何修改的dispatch方法,最后返回一个经过了修改的新的dispatch方法。

    什么是compose?在函数式编程中,compose指接收多个函数作为参数,并返回一个新的函数的方式。调用新函数后传入一个初始的值作为参数,该参数经最后一个函数调用,将结果返回并作为倒数第二个函数的入参,倒数第二个函数调用完后,将其结果返回并作为倒数第三个函数的入参,依次调用,知道最后调用完传入compose的所有的函数后,返回一个最后的结果。

    compose函数如下:
    [...chain].reduce((a, b) => (...args) => a(b(...args)))
    实际就是一个柯里化函数,即将所有的middleware合并成一个middleware,并在最后一个middleware中传入当前的dispatch

    // 假设chain如下:
    chain = [
        a: next => action => { console.log('第1层中间件') return next(action) }
        b: next => action => { console.log('第2层中间件') return next(action) }
        c: next => action => { console.log('根dispatch') return next(action) }
    ]
    

    调用compose(...chain)(store.dispatch)后返回a(b(c(dispatch)))
    可以发现已经将所有middleware串联起来了,并同时修改了dispatch的指向。
    最后看一下这时候compose执行返回,如下:

    dispatch = a(b(c(dispatch)))
    
    // 调用dispatch(action)
    // 执行循序
    /*
       1. 调用 a(b(c(dispatch)))(action) __print__: 第1层中间件
       2. 返回 a: next(action) 即b(c(dispatch))(action)
       3. 调用 b(c(dispatch))(action) __print__: 第2层中间件
       4. 返回 b: next(action) 即c(dispatch)(action)
       5. 调用 c(dispatch)(action) __print__: 根dispatch
       6. 返回 c: next(action) 即dispatch(action)
       7. 调用 dispatch(action)
    */
    

    总结来说就是:

    在中间件串联的时候,middleware1-3的串联顺序是从右至左的,也就是middleware3被包裹在了最里面,它内部含有对原始的store.dispatch的调用,middleware1被包裹在了最外边。

    当我们在业务代码中dispatch一个action时,也就是中间件执行的时候,middleware1-3的执行顺序是从左至右的,因为最后被包裹的中间件,将被最先执行。

    如图所示:
    在这里插入图片描述

    3、常见的redux中间件

    3.1、logger日志中间件

    源码:

    function createLogger(options = {}) {
      /**
       * 传入 applyMiddleWare 的函数
       * @param  {Function} { getState      }) [description]
       * @return {[type]}      [description]
       */
      return ({ getState }) => (next) => (action) => {
        let returnedValue;
        const logEntry = {};
        logEntry.prevState = stateTransformer(getState());
        logEntry.action = action;
        // .... 
        returnedValue = next(action);
        // ....
        logEntry.nextState = stateTransformer(getState());
        // ....
        return returnedValue;
      };
    }
    
    export default createLogger;
    

    为了方便查看,将代码修改为ES5之后,如下:

    /**
     * getState 可以返回最新的应用 store 数据
     */
    function ({getState}) {
        /**
         * next 表示执行后续的中间件,中间件有可能有多个
         */
        return function (next) {
            /**
             * 中间件处理函数,参数为当前执行的 action 
             */
            return function (action) {...}
        }
    }
    

    这样的结构本质上就是为了将 middleware 串联起来执行。

    3.2、redux异步管理中间件

    在多种中间件中,处理 redux 异步事件的中间件,绝对占有举足轻重的地位。从简单的 react-thunk 到 redux-promise 再到 redux-saga等等,都代表这各自解决redux异步流管理问题的方案。

    3.2.1、redux-thunk

    redux-thunk的使用:

    function getWeather(url, params) {
        return (dispatch, getState) => {
            fetch(url, params)
                .then(result => {
                    dispatch({
                        type: 'GET_WEATHER_SUCCESS', payload: result,
                    });
                })
                .catch(err => {
                    dispatch({
                        type: 'GET_WEATHER_ERROR', error: err,
                    });
                });
            };
    }
    

    在上述使用实例中,我们应用thunk中间到redux后,可以dispatch一个方法,在方法内部我们想要真正dispatch一个action对象的时候再执行dispatch即可,特别是异步操作时非常方便。

    源码:

    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;
    

    为了方便阅读,源码中的箭头函数在这里换成了普通函数,如下:

    function createThunkMiddleware (extraArgument){
        return function ({dispatch, getState}){
            return function (next){
                return function (action){
                    if (typeof action === 'function'){
                        return action(dispatch, getState, extraArgument);
                    }
                    return next(action);
                };
            }
        }
    }
    
    let thunk = createThunkMiddleware();
    thunk.withExtraArgument = createThunkMiddleware;
    
    export default thunk;
    

    thunk是一个很常用的redux中间件,应用它之后,我们可以dispatch一个方法,而不仅限于一个纯的action对象。它的源码也很简单,如上所示,除去语法固定格式也就区区几行。

    下面我们就来看看源码(为了方便阅读,源码中的箭头函数在这里换成了普通函数),首先是这三层柯里化:

    // 外层
    function createThunkMiddleware (extraArgument){
         // 第一层
        return function ({dispatch, getState}){
           // 第二层
            return function (next){
                // 第三层
                return function (action){
                    if (typeof action === 'function'){
                        return action(dispatch, getState, extraArgument);
                    }
                    return next(action);
                };
            }
        }
    }
    

    首先是外层,上面的源码可知,这一层存在的主要目的是支持在调用applyMiddleware并传入thunk的时候时候可以不直接传入thunk本身,而是先调用包裹了thunk的函数(第一层柯里化的父函数)并传入需要的额外参数,再将该函数调用的后返回的值(也就是真正的thunk)传给applyMiddleware,从而实现对额外参数传入的支持,使用方式如下:

    const store = createStore(reducer, applyMiddleware(thunk.withExtraArgument({api, whatever})))

    如果无需额外参数则用法如下:

    const store = createStore(reducer, applyMiddleware(thunk))

    接下来来看第一层,这一层是真正applyMiddleware能够调用的一层,从形参来看,这个函数接收了一个类似于store的对象,因为这个对象被结构以后获取了它的dispatch和getState这两个方法,巧的是store也有这两方法,但这个对象到底是不是store,还是只借用了store的这两方法合成的一个新对象?这个问题在我们后面分析applyMiddleware源码时,自会有分晓。

    再来看第二层,在第二层这个函数中,我们接收的一个名为next的参数,并在第三层函数内的最后一行代码中用它去调用了一个action对象,感觉有点 dispatch({type: 'XX_ACTION', data: {}}) 的意思,因为我们可以怀疑它就是一个dispatch方法,或者说是其他中间件处理过的dispatch方法,似乎能通过这行代码链接上所有的中间件,并在所有只能中间件自身逻辑处理完成后,最终调用真实的 store.dispath 去dispatch一个action对象,再走到下一步,也就是reducer内。

    最后我们看看第三层,在这一层函数的内部源码中首先判断了action的类型,如果action是一个方法,我们就调用它,并传入dispatch、getState、extraArgument三个参数,因为在这个方法内部,我们可能需要调用到这些参数,至少dispatch是必须的。**这三行源码才是真正的thunk核心所在。所有中间件的自身功能逻辑也是在这里实现的。**如果action不是一个函数,就走之前解析第二层时提到的步骤。

    3.2.2、redux-promise

    不同的中间件都有着自己的适用场景,react-thunk 比较适合于简单的API请求的场景,而 Promise 则更适合于输入输出操作,比较fetch函数返回的结果就是一个Promise对象,下面就让我们来看下最简单的 Promise 对象是怎么实现的:

    import { isFSA } from 'flux-standard-action';
    
    function isPromise(val) {
      return val && typeof val.then === 'function';
    }
    
    export default function promiseMiddleware({ dispatch }) {
      return next => action => {
        if (!isFSA(action)) {
          return isPromise(action)
            ? action.then(dispatch)
            : next(action);
        }
    
        return isPromise(action.payload)
          ? action.payload.then(
              result => dispatch({ ...action, payload: result }),
              error => {
                dispatch({ ...action, payload: error, error: true });
                return Promise.reject(error);
              }
            )
          : next(action);
      };
    }
    

    它的逻辑也很简单主要是下面两部分:

    1. 先判断是不是标准的 flux action。如果不是,那么判断是否是 promise, 是的话就执行 action.then(dispatch),否则执行 next(action)
    2. 如果是, 就先判断 payload 是否是 promise,如果是的话 payload.then 获取数据,然后把数据作为 payload 重新 dispatch({ ...action, payload: result});不是的话就执行 next(action)

    结合 redux-promise 我们就可以利用 es7 的 async 和 await 语法,来简化异步操作了,比如这样:

    const fetchData = (url, params) => fetch(url, params)
    async function getWeather(url, params) {
        const result = await fetchData(url, params)
        if (result.error) {
            return {
                type: 'GET_WEATHER_ERROR', error: result.error,
            }
        }
            return {
                type: 'GET_WEATHER_SUCCESS', payload: result,
            }
        }
    

    3.2.3、redux-saga

    redux-saga是一个管理redux应用异步操作的中间件,用于代替 redux-thunk 的。它通过创建 Sagas 将所有异步操作逻辑存放在一个地方进行集中处理,以此将react中的同步操作与异步操作区分开来,以便于后期的管理与维护。对于Saga,我们可简单定义如下:

    Saga = Worker + Watcher

    redux-saga相当于在Redux原有数据流中多了一层,通过对Action进行监听,从而捕获到监听的Action,然后可以派生一个新的任务对state进行维护(这个看项目本身的需求),通过更改的state驱动View的变更。如下图所示:
    在这里插入图片描述

    saga特点:

    1. saga 的应用场景是复杂异步。
    2. 可以使用 takeEvery 打印 logger(logger大法好),便于测试。
    3. 提供 takeLatest/takeEvery/throttle 方法,可以便利的实现对事件的仅关注最近实践还是关注每一次实践的时间限频。
    4. 提供 cancel/delay 方法,可以便利的取消或延迟异步请求。
    5. 提供 race(effects),[…effects] 方法来支持竞态和并行场景。
    6. 提供 channel 机制支持外部事件。
    function *getCurrCity(ip) {
        const data = yield call('/api/getCurrCity.json', { ip })
        yield put({
            type: 'GET_CITY_SUCCESS', payload: data,
        })
    }
    function * getWeather(cityId) {
        const data = yield call('/api/getWeatherInfo.json', { cityId })
        yield put({
            type: 'GET_WEATHER_SUCCESS', payload: data,
        })
    }
    function loadInitData(ip) {
        yield getCurrCity(ip)
        yield getWeather(getCityIdWithState(state))
        yield put({
            type: 'GET_DATA_SUCCESS',
        })
    }
    

    总的来讲Redux Saga适用于对事件操作有细粒度需求的场景,同时它也提供了更好的可测试性,与可维护性,比较适合对异步处理要求高的大型项目,而小而简单的项目完全可以使用redux-thunk就足以满足自身需求了。毕竟react-thunk对于一个项目本身而言,毫无侵入,使用极其简单,只需引入这个中间件就行了。而react-saga则要求较高,难度较大。

    展开全文
  • redux-websocket是用于通过WebSocket连接管理数据的Redux中间件。 该中间件使用动作与WebSocket连接进行交互,包括连接,断开连接,发送消息和接收消息。 所有动作均遵循模型。 产品特点 用TypeScript编写。 通过...
  • React:Redux中间件

    2022-06-08 22:56:43
    结合 应用实例与 源码,对 中间件的实现原理进行分析。在此基础上,对“面向切面”这一经典的编程思想建立初步的认识。  在之前介绍 函数时,已经简单地提过中间件——中间件相关的信息将作为 函数的一个 类型的入...
  • Redux概念 Redux用于存储各个组件的数据,将数据存到一个公共的store进行管理, Redux = Reducer + Flux。 Redux 的工作流程 dispatch(actions) (previousState, action) Action Creators ---------------->...
  • Redux及React-Redux 一、Redux、React-Redux 分别是什么? 1)Redux 是 JavaScript 状态容器,提供可预测化的状态管理。由Flux演变而来。 Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、...
  • redux-axios-中间件 Redux中间件,用于使用axios HTTP客户端获取数据 安装 npm i -S redux-axios-...默认情况下,您只需要从包中导入中间件并将其添加到redux中间件中,并使用第一个参数与axios实例一起执行即可。 第
  • 使用Redux中间件,以便任何改变导航状态的事件都能正确触发React Navigation的事件监听器。 最后,Reducer使React Navigation动作可以改变Redux状态。动机同时使用Redux和React Navigation的大多数项目不需要此库
  • redux的核心概念其实很简单:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store....
  • Redux中间件向socket.io服务器发出操作 API 应用中间件 import io from 'socket.io-client' ; import { createStore , applyMiddleware } from 'redux' ; import socketIO from 'socket.io-redux' ; import reducer...
  • 用于Redux中间件 安装 $ npm install redux-xstate 用法 import { createMiddleware , createReducer } from 'redux-xstate' const actionMap = { log : ( dispatch , state ) => fetch ( LOG_URL , { method : ...
  • [Redux/Mobx] Redux的中间件是什么?你有用过哪些Redux的中间件? view在redux中会派发一个action,...redux中间件的中间指的是action和store之间,之前在redux中action只能返回一个对象,所以action返回的一个对象会
  • 1、Redux-thunk中间件 第一步 安装redux-thunk中间件 npm install redux-thunk 第二步 在store中引入thunk组件 import {createStore,applyMiddleware } from 'redux'; import Reducer from './Reducer'; import ...
  • 采用Redux中间件 为什么要使用中间件 在利用Redux进行状态管理时,用户在UI层面触发行为,一个action对象通过store.dispatch派发到Reducer进行触发,接下来Reducer会根据type来更新对应的Store上的状态树,更改后的...
  • refx是用于触发副作用的Redux中间件。 在Redux中,操作是同步分派的。 但是,在许多实际应用程序中,您会发现自己需要异步地从API提取数据。 生态系统中其他流行的解决方案(如增强您的能力,使他们能够根据需要...
  • 还原操作概述redux-undo-actions是用于撤消/重做操作的Redux中间件。 它不会将状态更改为 。 而不是更改应用程序的状态,而是调度与历史记录相反的操作。安装要开始使用redux-undo-actions,您需要运行以下命令: ...
  • 一个简单的redux中间件及reduxAPI
  • 学习Redux中间件

    2021-02-25 04:01:48
    Create React App入门该项目是通过。可用脚本在项目目录中,可以运行:npm start 在开发模式下运行应用程序。 打开在浏览器中查看。 如果进行编辑,页面将重新加载。 您还将在控制台中看到任何棉绒错误。...
  • Redux中间件,用于在Redux存储的空闲期间将要分派的操作排队。 :woman::laptop: 录用 :man::laptop: 想要为您对redux-idle-callback贡献获得报酬吗? 将您的简历发送到 :open_book: 目录 :sparkles: 特征 允许...
  • 主要介绍了简单介绍redux中间件的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 1. Redux中间件 1.1 什么是中间件 ? 中间件其实就是一个函数,中间件允许我们扩展redux应用程序 。具体体现在对action的处理能力上,当组件触发一个action后,这个action会优先被中间件处理,当中间件处理完后,...
  • redux-api-中间件 用于调用API的。 该中间件接收(RSAA),并将(FSA)调度到下一个中​​间件。 RSAA通过[RSAA]属性的存在来标识,其中是redux-api-middleware定义的String常量,并由redux-api-middleware导出。 ...
  • 符合的Redux中间件,可减轻跟踪异步操作状态的麻烦。 尽管对于中间件的表示方式的合规性似乎有所破坏(例如,它需要types的字段而不是type ),但与之一起分派的每个操作都完全符合FSA。 您可以将这种中间件视为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,574
精华内容 3,429
关键字:

自己写redux中间件