• react中间件的概念

    2017-12-19 15:13:58
    为了理解中间件,让我们站在框架作者的角度思考问题:如果要添加功能,你会在哪个环节添加? (1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行...

    一、中间件的概念

    为了理解中间件,让我们站在框架作者的角度思考问题:如果要添加功能,你会在哪个环节添加?

    (1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。

    (2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。

    (3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

    想来想去,只有发送 Action 的这个步骤,即store.dispatch()方法,可以添加功能。举例来说,要添加日志功能,把 Action 和 State 打印出来,可以对store.dispatch进行如下改造。

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

    上面代码中,对store.dispatch进行了重定义,在发送 Action 前后添加了打印功能。这就是中间件的雏形。

    中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

    二、中间件的用法

    本教程不涉及如何编写中间件,因为常用的中间件都有现成的,只要引用别人写好的模块即可。比如,上一节的日志中间件,就有现成的redux-logger模块。这里只介绍怎么使用中间件。

    
    import { applyMiddleware, createStore } from 'redux';
    import createLogger from 'redux-logger';
    const logger = createLogger();
    
    const store = createStore(
      reducer,
      applyMiddleware(logger)
    );
    

    上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

    这里有两点需要注意:

    (1)createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。

    
    const store = createStore(
      reducer,
      initial_state,
      applyMiddleware(logger)
    );
    

    (2)中间件的次序有讲究。

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

    上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确。

    三、applyMiddlewares()

    看到这里,你可能会问,applyMiddlewares这个方法到底是干什么的?

    它是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。下面是它的源码。

    
    export default function applyMiddleware(...middlewares) {
      return (createStore) => (reducer, preloadedState, enhancer) => {
        var store = createStore(reducer, preloadedState, enhancer);
        var dispatch = store.dispatch;
        var chain = [];
    
        var middlewareAPI = {
          getState: store.getState,
          dispatch: (action) => dispatch(action)
        };
        chain = middlewares.map(middleware => middleware(middlewareAPI));
        dispatch = compose(...chain)(store.dispatch);
    
        return {...store, dispatch}
      }
    }
    

    上面代码中,所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getStatedispatch这两个方法。

    四、异步操作的基本思路

    理解了中间件以后,就可以处理异步操作了。

    同步操作只要发出一种 Action 即可,异步操作的差别是它要发出三种 Action。

    • 操作发起时的 Action
    • 操作成功时的 Action
    • 操作失败时的 Action

    以向服务器取出数据为例,三种 Action 可以有两种不同的写法。

    
    // 写法一:名称相同,参数不同
    { type: 'FETCH_POSTS' }
    { type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
    { type: 'FETCH_POSTS', status: 'success', response: { ... } }
    
    // 写法二:名称不同
    { type: 'FETCH_POSTS_REQUEST' }
    { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
    { type: 'FETCH_POSTS_SUCCESS', response: { ... } }
    

    除了 Action 种类不同,异步操作的 State 也要进行改造,反映不同的操作状态。下面是 State 的一个例子。

    
    let state = {
      // ... 
      isFetching: true,
      didInvalidate: true,
      lastUpdated: 'xxxxxxx'
    };
    

    上面代码中,State 的属性isFetching表示是否在抓取数据。didInvalidate表示数据是否过时,lastUpdated表示上一次更新时间。

    现在,整个异步操作的思路就很清楚了。

    • 操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染
    • 操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲染

    五、redux-thunk 中间件

    异步操作至少要送出两个 Action:用户触发第一个 Action,这个跟同步操作一样,没有问题;如何才能在操作结束时,系统自动送出第二个 Action 呢?

    奥妙就在 Action Creator 之中。

    
    class AsyncApp extends Component {
      componentDidMount() {
        const { dispatch, selectedPost } = this.props
        dispatch(fetchPosts(selectedPost))
      }
    
    // ...
    

    上面代码是一个异步组件的例子。加载成功后(componentDidMount方法),它送出了(dispatch方法)一个 Action,向服务器要求数据 fetchPosts(selectedSubreddit)。这里的fetchPosts就是 Action Creator。

    下面就是fetchPosts的代码,关键之处就在里面。

    
    const fetchPosts = postTitle => (dispatch, getState) => {
      dispatch(requestPosts(postTitle));
      return fetch(`/some/API/${postTitle}.json`)
        .then(response => response.json())
        .then(json => dispatch(receivePosts(postTitle, json)));
      };
    };
    
    // 使用方法一
    store.dispatch(fetchPosts('reactjs'));
    // 使用方法二
    store.dispatch(fetchPosts('reactjs')).then(() =>
      console.log(store.getState())
    );
    

    上面代码中,fetchPosts是一个Action Creator(动作生成器),返回一个函数。这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行异步操作。拿到结果后,先将结果转成 JSON 格式,然后再发出一个 Action( receivePosts(postTitle, json))。

    上面代码中,有几个地方需要注意。

    (1)fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。

    (2)返回的函数的参数是dispatchgetState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。

    (3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。

    (4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。

    这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。

    这时,就要使用中间件redux-thunk

    
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import reducer from './reducers';
    
    // Note: this API requires redux@>=3.1.0
    const store = createStore(
      reducer,
      applyMiddleware(thunk)
    );
    

    上面代码使用redux-thunk中间件,改造store.dispatch,使得后者可以接受函数作为参数。

    因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch

    六、redux-promise 中间件

    既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。

    这就需要使用redux-promise中间件。

    
    import { createStore, applyMiddleware } from 'redux';
    import promiseMiddleware from 'redux-promise';
    import reducer from './reducers';
    
    const store = createStore(
      reducer,
      applyMiddleware(promiseMiddleware)
    ); 
    

    这个中间件使得store.dispatch方法可以接受 Promise 对象作为参数。这时,Action Creator 有两种写法。写法一,返回值是一个 Promise 对象。

    
    const fetchPosts = 
      (dispatch, postTitle) => new Promise(function (resolve, reject) {
         dispatch(requestPosts(postTitle));
         return fetch(`/some/API/${postTitle}.json`)
           .then(response => {
             type: 'FETCH_POSTS',
             payload: response.json()
           });
    });
    

    写法二,Action 对象的payload属性是一个 Promise 对象。这需要从redux-actions模块引入createAction方法,并且写法也要变成下面这样。

    
    import { createAction } from 'redux-actions';
    
    class AsyncApp extends Component {
      componentDidMount() {
        const { dispatch, selectedPost } = this.props
        // 发出同步 Action
        dispatch(requestPosts(selectedPost));
        // 发出异步 Action
        dispatch(createAction(
          'FETCH_POSTS', 
          fetch(`/some/API/${postTitle}.json`)
            .then(response => response.json())
        ));
      }
    

    上面代码中,第二个dispatch方法发出的是异步 Action,只有等到操作结束,这个 Action 才会实际发出。注意,createAction的第二个参数必须是一个 Promise 对象。

    看一下redux-promise源码,就会明白它内部是怎么操作的。

    
    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);
      };
    }
    

    从上面代码可以看出,如果 Action 本身是一个 Promise,它 resolve 以后的值应该是一个 Action 对象,会被dispatch方法送出(action.then(dispatch)),但 reject 以后不会有任何动作;如果 Action 对象的payload属性是一个 Promise 对象,那么无论 resolve 和 reject,dispatch方法都会发出 Action。

    中间件和异步操作,就介绍到这里。下一篇文章将是最后一部分,介绍如何使用react-redux这个库。

    展开全文
  • 为什么使用reselect 在使用redux时,我们通常会创建很多selector来从store中读取数据。下面的代码取自于redux的todomvc示例。 const getVisibleTodos = (todos, filter) => { switch (filter) { ...

    为什么使用reselect

    在使用redux时,我们通常会创建很多selector来从store中读取数据。下面的代码取自于redux的todomvc示例。

    const getVisibleTodos = (todos, filter) => {
      switch (filter) {
        case 'SHOW_ALL':
          return todos
        case 'SHOW_COMPLETED':
          return todos.filter(t => t.completed)
        case 'SHOW_ACTIVE':
          return todos.filter(t => !t.completed)
      }
    }

    这个代码有一个潜在的问题。每当state tree改变时,selector都要重新运行。当state tree特别大,或者selector计算特别耗时,那么这将带来严重的运行效率问题。

    为了解决这个问题,reselect为selector设置了缓存,只有当selector的输入改变时,程序才重新调用selector函数。

    使用方法:

    使用reselect重写getVisibleTodos

    import { createSelector } from 'reselect'
    
    const getVisibilityFilter = (state) => state.visibilityFilter
    const getTodos = (state) => state.todos
    
    export const getVisibleTodos = createSelector(
      [ getVisibilityFilter, getTodos ],
      (visibilityFilter, todos) => {
        switch (visibilityFilter) {
          case 'SHOW_ALL':
            return todos
          case 'SHOW_COMPLETED':
            return todos.filter(t => t.completed)
          case 'SHOW_ACTIVE':
            return todos.filter(t => !t.completed)
        }
      }
    )

    更多详细资料请参考:
    https://github.com/reactjs/reselect
    https://www.jianshu.com/p/8d89c67dfefd

    展开全文
  • 浅析react中间件机制

    2019-07-03 13:34:08
    看过react的人都知道, react是一个view层的展现库,要想实现对页面数据和路由的管理还需要配合其它的库。这其中最常用的就是redux和react-router库。 通过redux库能够统一管理页面数据,保证数据的单向流动,其...

          看过react的人都知道, react是一个view层的展现库,要想实现对页面数据和路由的管理还需要配合其它的库。这其中最常用的就是redux和react-router库。

          通过redux库能够统一管理页面数据,保证数据的单向流动,其大概流程是 用户触发页面交互,页面根据用户交互产生一个action并将这个action通过store的dispatch方法传给

    sotre,store根据一定关系找到reducer,reducer根据action的type类型产生新的state值并将新产生的state值回传给store,store根据最新的state通知view重新渲染页面(通过调用render函数)。而react-rouer则用来控制react中路由跳转。这样通过react redux react-router相互配合形成类似于mvc的前端页面结构。

         在上面的流程中store是数据管理的源头,但是store的数据又来自哪里呢?可以说绝大多数数据来自服务端,这里又可以分为两类数据,一类是在页面初始化的时候由服务端直接提供的数据,另一类是通过用户交互从服务端获取的数据。对于第二类数据主要是通过异步方式(ajax或者promise或者生成器或者await/async)获取的数据。今天主要记录一下自己对react和redux配合使用时如何将异步获取数据流程融合进去的理解。

        在用户操作页面得到结果的过程中主要经历了以下几个主要过程 1.用户交互产生action 2.dispatch分发action 3.reducer产生新的state值 4.storoe通知view重新渲染页面。如果要把异步获取数据的操作融合进去只有1 2两个阶段,而这两个阶段中只有在产生action之后 分发action之前是最合适的时机。要想了解这其中的运行机制,就必须了解react中间件的原理。中间件很好理解,就是一个处理过程,有输入有输出,但是中间加入了一些其它操作,类似于设计模式中的装饰模式。经过中间件的处理后,能够增加一些其它功能,比如日志记录功能,数据上报功能等等。

        我们在学习react的时候会看到很多关于如何 使用react和redux的示例代码,在创建store的时候有些是这样的:

     const store=createStore1(rootReducer,initialState);

      也有些是这样:

    createStore1 = applyMiddleware(thunk)(createStore)(rootReducer,initialState);

     第一种方式是中规中矩的示例写法,创建了一个具有最基本功能的store对象。第二种方式为store加入了其它功能,加入这个功能使用的就是redux的中间件函数 applyMiddleware,这也是react支持高扩展性的关键,可以通过applyMiddleware函数为react应用添加多种功能,而它的实现方式主要是通过高阶组件实现的。我们可能头一次听说高阶组件这个词汇,但是高阶函数相信大多人都听说过,也写过,高阶函数中是把一个函数作为参数传入,经过修饰产生一个新的函数,同理如果传入是react组件输出的也是经过修饰的新组件那么就可以称为高阶组件(个人理解),类似于装饰模式。接下来让我们一起分析下这个applylMiddleware是个什么东西,怎么就为react应用添加了其它功能。

         先贴出applyMiddleware的实现代码:

     1 export default function applyMiddleware(...middlewares) {
     2   return (createStore) => (reducer, preloadedState, enhancer) => {
     3     const store = createStore(reducer, preloadedState, enhancer)
     4     let dispatch = store.dispatch
     5     let chain = []
     6 
     7     const middlewareAPI = {
     8       getState: store.getState,
     9       dispatch: (action) => dispatch(action)
    10     }
    11     chain = middlewares.map(middleware => middleware(middlewareAPI))
    12     dispatch = compose(...chain)(store.dispatch)
    13 
    14     return {
    15       ...store,
    16       dispatch
    17     }
    18   }
    19 }

    上面代码中是es6写法,export用于导出函数 类似于module.exports的作用,在另一个文件中用import进行导入。参数的...middleware是扩展运算符可以把数组转成对应的参数。=>是箭头函数。对于export  ... 都好理解,对于return加上两个箭头函数的组合,可能还不是太适应,下面把这部分转成我们熟悉的形式:

    export default function applyMiddleware(...middlewares) {
    return function(createStore) {
    return funciton(reducer, preloadedState, enhancer) {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
    getState: store.getState,
    dispatch: (action) = > dispatch(action)
    }
    chain = middlewares.map(middleware = > middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
    ...store,
    dispatch
    }
    }
    }
    }

      通过改成我们熟悉的格式可以很直观的发现所谓的中间件就是传入一些参数 然后返回了一个函数(第一个函数fun1),而返回的这个函数再次传入一些参数又返回了一个新的函数(第二个函数 fun2),fun2再次传入一些参数就会执行最内层的函数体。对照 applyMiddleware(thunk)(createStore)(rootReducer,initialState);我们可以做相应的替换其中applyMiddleware(thunk)可以替换成fun1,替换后的结果是是fun1(createStore)(rootReducer,initialState) 然后把fun1(createStore)替换成fun2 其结果fun2(rootReducer,initialState) 这样我们逐层分解就能得到我们最熟悉最简单的函数调用形式。通过这种高阶函数的调用在内部可以保持对外层传入参数的引用,在需要使用的时候调用,可以理解为对外层传入参数的延迟使用。接下来我们看一下经过三层参数的传入在最内层到底做了些什么事情。在此提醒一下,不要被参数名误导,它只是一个参数,不具体指任何对象。applyMiddlleware的函数只有这么几行,让我们一行一行的分析。

           还是从applyMiddleware(thunk)(createStore)(rootReducer,initialState);这个代码说起,第一层数传入的是thunk,只有一个,为了和applyMiddleware的源码参数保持一致,我们假定thunk是一个数组,第二 层参数是createStore 是redux的核心函数之一,用于创建store,第三层参数是rootReducer,initialState 对应applyMiddleware源码中的reducer, preloadedState, enhancer,只不过一个传入两个参数一个三个参数,这些都不重要,目前我们只使用第一个参数。

          函数体内第一行代码 const store = createStore(reducer, preloadedState, enhancer) 用传入的第二层参数createStore(此参数是一个函数)和第三层参数创建一个store对象,用真是参数代替就是 const store = createStore(rootReducer,initialState) 。

         第二句代码 let dispatch = store.dispatch;保存store的dispatch函数的引用。

        第三句代码 let chain = [] 建立一个空数组对象。

        第四句代码  

    const middlewareAPI = {
                    getState: store.getState,
                    dispatch: (action) = > dispatch(action)
            }
    

      定义一个对象,对象有两个属性,都是函数 一个sotre的getState函数用于返回store,一个dispatch函数。

         第五句代码  chain = middlewares.map(middleware = > middleware(middlewareAPI)) 遍历第一层参数传入的数组,数组中是用到的各种中间件函数,中间件函数接受第四句代码定义的middlewareAPI对象作为入参,最终返回一个有一系列中间件函数返回的结果组成的数组对象。

        第六句代码 dispatch = compose(...chain)(store.dispatch) 将第五句代码产生的chain作为第一层参数 store.dispatch作为第二层参数,这里的第一层参数和第二层参数要和上面的第一层 第二层 区分开,本文中提到的第几层参数都指上面的提到的参数。compose是一个组合函数经过compose的高阶函数调用返回的结果赋值给第二句代码中的dispatch对象,注意经过 2 3 4 5 步后 dispatch对象已经被重新定义。

        第七句代码 return {...store, dispatch} 返回新的store对象,其中dispatch会覆盖store中的dispatch对象。至此一共七句代码执行完毕 我们最终得到一个增强的store对象。那中间件函数是如何发挥作用的呢,奥秘就在第五句和第六句代码中,让我们继续解析。

      在解析之前先让我们看看 react中间件长什么样子,以下是一个最简单的示例:

    export default sotre=>next=>action=>{
         console.log('someInfo');
         next(action);
         consoe.log('someInfoOther');
    }
    

     react中中间件也是以高阶组件的形式出现,并使用es6语法,如果 看着不服输可以按照上面的方式将es6改成我们熟悉的es5形式。

    export default function (sotre){
          return function(next){            //fun1
              return function(action){       //fun2
                    console.log('someInfo');
                   next(action);
                   consoe.log('someInfoOther');
              }
         }
    }
    

      

    好了我们继续回到applyMiddleware函数的第五句代码 chain = middlewares.map(middleware = > middleware(middlewareAPI)) 中间件的数组执行以后返回一个新数组,按照上面中间件的形式套进去 我们会得到一个 fun1函数的数组我们记为fun1Arr([f1,f2,.....fn])。我们再来看applyMiddleware的第六句代码 dispatch = compose(...chain)(store.dispatch) ,将上一步返回的函数数组fun1Arr传入函数compose(理解这个函数很重要)中,我们继续看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)))
    }
    

      这个函数也很简单前两个if 可以直接忽略,最后一句的reduce函数也很简单,就是数组的reduce函数,简单介绍一下,语法格式为:

    arr.reduce([callback, initialValue])

    callback是执行数组中每个值的函数,包括四个参数 1.previousValue上一次调用回调函数返回的值,或者是提供的初始值(initialValue) 2.currentValue数组中当前被处理的元素 3.currentIndex当前被处理元素在数组中的索引,
     即currentValue的索引.如果有initialValue初始值, 从0开始.如果没有从1开始 4.arr 当前调用的数组。
    initialValue是传入的初始值。这个函数看着很简单,但当把fun1Arr传入就不简单了。注意reduce里是有两个箭头函数的函数。为了便于理解
    我们改成es5形式
    return funcs.reduce(function(a,b){
          return function(...args){     //fr_x_y 函数
               a(b(...args));
          }
    })
    

      对应reduce的原型函数可以发现 只有一个callback参数,没有initialValue参数,对于没有initialValue值得reduce函数 回调函数第一次执行时,previousValue 和 currentValue可能是两个不同值其中的一个,如果reduceinitialValue参数,那么 previousValue 等于 initialValue ,并且currentValue 等于数组中的第一个值;如果reduce没有 initialValue 参数,那么previousValue 等于数组中的第一个值,currentValue等于数组中的第二个值。对于fun1Arr传入compose函数后得到一个层层调用的函数,其形式为:

    f1(f2(f3(fn(...args)))),注:fun1Arr的形式为[f1,f2,f3,....fn]。至此我们得到了compose执行后最终的函数 形式f1(f2(f3(fn(...args)))),其中fn表示applyMiddleware中传入的中间件函数。再回过头来看applyMiddleware的第6句代码dispatch = compose(...chain)(store.dispatch) ,其中compose(...chain)返回的就是f1(f2(f3(fn(...args))))这个函数,最后传入store.dispatch参数将返回的结果又赋值给dispatch,这里最终是对dispatch进行了一次改写,换句话说就是通过高阶函数增强了dispatch,让原本单调的dispatch函数变得丰富起来。applyMiddleware函数的最后将增强后的dispath函数重新赋值给store返回。接下来我们结合中间件的实现方式 将f1(f2(f3(fn(store.dispatch)))) 【将...args替换成store.dispatch】函数展开。上面我们已经有了中间件函数的形式(高阶函数)并且在applyMiddleware中第5句代码处执行过一次中间件函数,所以这里的fn对应的函数格式就是

    return function(next){            //fun1
              return function(action){       //fun2
                    console.log('someInfo');
                   next(action);
                   consoe.log('someInfoOther');
              }
         }
    

     将上面的fun1函数带入f1(f2(f3(fn(store.dispatch)))) 函数后层层展开 我们最终得到一个新函数,其格式为

    function(action){
            中间件1执行的操作
            中间件2执行的操作
           ........
             中间件n执行的操作
               store.dispatch(action);
    }
    

      所以store中dispatch被增强后的形式就是上面的形式,在view中调用dispath(action)的时候就是上面函数执行的过程。至此我们把react中间件的流程梳理了一遍。在文章的开头我们讲到如何将react   redux  异步请求数据结合到一起,现在我们理解了中间件的过程,再说异步请求数据是如何整合到redux中就简单许多了。还是先看一下异步中间件的源码我们以react-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;
    

      通过源码可以发现异步中间件只是对action做了一个判断如果action是function 那就执行这个函数,并把dispatch 和 getState传入函数。这里需要注意一点如果action是函数就会自行此函数 不会next(action)了 所以异步中间件要放在所有中间件的最后,面得其它功能型中间件不起作用。

    好了,终于把react中间件的主要流程梳理完了,最后再补充一句 高阶函数是层层调用的 每层调用时传入的参数都会随着层数的不同而不同。


    转载于:https://www.cnblogs.com/miaozhigao/p/7487792.html

    展开全文
  • react中间件

    2018-04-18 10:12:26
    中间件

    中间件

    中间件是对store.dispatch的改造
    promiseMiddleware
    其实上面的 thunk 我们已经有了处理异步的能力, 但是每次我们要自己去手动触发三个 action, 工作量还是很大的。现在 ajax 很多都会包装为 promise 对象, 因此我们可以对与 dispatch 增加一层判断, 使得它具有处理具有 promise 属性的 action 的能力。

        store.dispatch = action => {
            if (isPromise(action.payload)) {
                const { type, payload, params } = action
                dispatch2({
                    type: `${type}_PENDDING`,
                    params
                })
                payload.then(
                    resolve => {
                        dispatch2({
                            type: `${type}_SUCCESS`,
                            content: resolve,
                            params
                        })
                    },
                    reject => {
                        dispatch2({
                            type: `${type}_ERROR`,
                            content: reject,
                            params
                        })
                    }
                )
            } else {
                dispatch2(action)
            }
        }
        ...
    
    

    redux-thunk源码
    加入redux-thunk后,action可以是函数了,依据redux-thunk的源码,我们可以看出如果传入的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;
    展开全文
  • React中间件 thunk&saga

    2018-12-27 10:25:27
    6-4 Redux 使用Redux-thunk中间件进行Ajax请求发送 引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。 thunk的原理,可以在actionCreators里通过返回一...
    • 6-4 Redux 使用Redux-thunk中间件进行Ajax请求发送

    引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。
    thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新。

    • npm add redux-thunk
    • gitHub中搜索redux-devtools-extension 1.2目录处复制使用中间件
    • 使用异步请求的中间件
    • store/index.js 使用 thunk
    import { createStore, applyMiddleware, compose} from "redux";
    import reducer from "./reducer";
    import thunk from "redux-thunk";
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
      : compose;
    
    const enhancer = composeEnhancers(
      applyMiddleware(thunk)
    );
    
    const store = createStore(reducer, enhancer);
    
    export default store;
    
    • actionCreators.js
    export const getTodoList = () => {
        return () => {
          axios.get("../../todoList.json")
          .then(res => {
              const data = res.data;
            const action = getAjax(data);
            store.dispatch(action);
          })
        }
    }
    
    • TodoList.js
    const action = getTodoList ();
    store.dispatch(action)
    
    • 6-5 什么是Redux的中间件
    • 用Redux Thunk 对Dispatch进行升级,这样就不仅可以使用对象,还能使用函数。基本没什么API,简单
      在这里插入图片描述
    • 6-6 Redux-saga 中间件的作用
    • GitHub :Redux-saga 非常大型项目更好用
    • npm add redux-saga
    • store/index.js配置
    import createSagaMiddleware from 'redux-saga';
    import todoSagas from './saga';
    // 创建 SagaMiddleware 
    const sagaMiddleware = createSagaMiddleware();
    // 使用 SagaMiddleware 
    const enhancer = composeEnhancers(
      applyMiddleware(thunk,sagaMiddleware)
    );
    // 创建sagaMiddleware 中间件
    const store = createStore(reducer, enhancer);
    // then run the saga 
    sagaMiddleware.run(todoSagas);
    
    • sagas.js
    import { takeEvery , put} from 'redux-saga/effects';
    import { GET_INIT_LIST } from './actionType'
    import { initListAction } from './actionCreators';
    import axios from 'axios';
    
    	// 一定要引入generrator 函数
    function* todoSaga() {
        // 捕捉action的类型 并 执行对应方法
        yield takeEvery(GET_INIT_LIST, getInitListSaga);
    }
    
        // 执行的方法
    function* getInitListSaga() {
        try {
            const res = yield axios.get('/todoList.json');
            const action = initListAction(res.data);
            // 传给store reducer进行处理
            yield put(action);
        }catch(e) {
            console.log('list.json 404');
        }
    }
    
    展开全文
  • react中间件

    2019-01-25 10:34:08
    概念: (1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。 (2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。...
  • react中间件 react-saga

    2018-09-07 12:21:48
    redux-saga 出发点跟 redux-thunk 是一样的,为了解决异步操作,把异步的逻辑单独的放到一个saga.js文件里面。 采用的是 generator 函数进行构建的。国内文档。 安装 yarn add redux-saga 使用: ...
  • Redux-thunk中间件ajax数据请求 安装这个插件就是为了返回一个函数,而不是只是对象。 先安装依赖 npm install redux-thunk --save 使用了中间件redux-thunk的作用是什么呢。就是让createAction中创建的action不...
  • 对于react-thunk中间件的简单理解 本人初次写博客,初衷旨在便于初学react-redux的同学能更快的理解其中的一些原理,并已假设读者已掌握redux的一些基本用法;如有错误,还望指出。不胜感激! 首先简单回顾一下...
  • react 中间件的应用

    2019-06-11 10:38:53
    以arc-web项目为例子 1:http/index.js 这个文件主要的作用是 发起http...const invokeApi = (参数1-配置参数) => (参数2-URL等请求参数) => {发起URL请求,返回请求结果} ...这个文件的作用主要是提供参数1 3:...
  • React/Redux项目结束后,当我在研究react-router源码的时候发现当中有一部分含中间件的思想,所以才想把中间件重新梳理一遍;在之前看redux了解到中间件,redux层面中间件理解对项目前期比较有帮助,虽然项目中...
  • React中间件

    2020-04-06 00:09:33
    中间件 import {createStore,applyMiddleware} from “redux”;//使用中间件import reducer from “./reducer/index.js”;import logger from “redux-logger”;// 能够在控制台直观的看到更改状态的type类型以及...
  • 1、redux-logger中间件的使用(日志打印辅助神器) 使用了这个中间件后,就会打印出操作日志
  • import React, {Component} from 'react'; //一步一步实现手写flux到使用插件编写 import Input1 from './Input1' import Input2 from './Input2' import Store from './store' import Dispatcher from ...
  • thunk中间件 转载于:https://juejin.im/post/5b7e81c351882542f933152b
  • react的redux中间件

    2019-09-25 03:39:18
    redux 的中间件(对dispatch功能的拦截和增强,一般不用自己写,有插件)dispath是一个方法,可以自行中间拦截更改:store.dispatch = function(action){console.log("dispatch执行了")next(action);console.log("执行...
  • universal-router - 一个简单的中间件风格路由器用于同构JavaScript的Web应用程序
  • 注:本教程针对于有过React/ReactNative开发经验的人群。阮一峰老师的博客: http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html其他中间件:redux-persistredux-sageredux-...
1 2 3 4 5 ... 20
收藏数 8,206
精华内容 3,282