A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

前言

首先简单回顾一下redux工作流程

对于reactUI组件来说,数据的来源无外乎两种,一种是用户主动触发的动作,例如点击事件、提交表单,输入操作;另一种是组件主动的数据更新,如获取页面初始数据,子组件接受父组件的props变化而进行更新视图操作;
如图所示,无论那种对于数据的操作,对于view都会派发出一个action

状态的更新
正如我们所知,在redux里,每次更新后的Store都会对应着一个新的view,而Store里面数据的更新依赖action的触发————Store.dispatch(action)会自执行初始化中createStore中注入的reducers,从而计算出新的状态。

[JavaScript] 纯文本查看 复制代码
import { createStore } from 'redux'

//reducer 计算状态的纯函数

//initialState 初始化数据

//enhancers中间件

createStore(reducers, initialState, enhancers)


action的使用和插件的扩展
对于组件的输入操作(如点击事件),可以将store.dispatch(action)绑定到组件

[JavaScript] 纯文本查看 复制代码
 const store = createStore(reducer);

 const TodoList = ({ state, someActionCreator }) => (

	    <ul>

        {state.map(someState =>

           <Todo

               key={someState.someData}

               onClick={() => store.dispatch(someActionCreator(state.someData))}

	         />

        </ul>

        ) 

或者通过connect方法,从组件的props中拿到dispatch方法,发出一个action
[JavaScript] 纯文本查看 复制代码
  //  将state注入到组件的props里

  //  注意,这里的state指的是redux管理的数据,每一个view的状态对应着

  //   唯一的state;

  //   state的集合就是redux管理的store

const mapStateToProps = store => ({

          state: store.state

})

​

 //  将action注入到组件的props 里

const mapDispatchToProps = dispatch => ({

  actions: state => dispatch(actionCreators(state))

})

​

export default connect(

  mapStateToProps,

  mapDispatchToProps

)(TodoList)

然后组件绑定事件就可以改成这样 ,( actionCreators用于生成action, 参考官方链接 https://redux.js.org/basics/actions)

[JavaScript] 纯文本查看 复制代码
 const TodoList = ({ state, actions }) => (

    `<ul>

        {state.map(someState =>

           <Todo

               key={someState.someData}

               onClick={() => actions(someState.someData)}

         />

        </ul>`

        ) 


那么问题来了,dispatch是同步执行reducers生成新状态的,对于页面的操作没有问题;但是如果点击事件是请求了某个结果,需要等待结果响应后再更新视图呢?应该如何处理?

因而redux引入了thunk中间件,对action进行了扩展
##thunk中间件解决了什么问题?
引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。
[JavaScript] 纯文本查看 复制代码
//未引入前的写法

let nextTodoId = 0

export const addTodo = text => ({

     type: 'ADD_TODO',

     id: nextTodoId++,

     text

 })

 

 

 //引入thunk后

 let nextTodoId = 0

 export const addTodo = text => ({

     return async dispatch => {

     //dosomething, request

      await request()

      dispatch({

	     type: 'ADD_TODO',

	     id: nextTodoId++,

	     text

		})

     }

 })

thunk中间件的使用方法
[JavaScript] 纯文本查看 复制代码
import { applyMiddleware, createStore } from 'redux';

import thunk from 'redux-thunk';



const store = createStore(

  reducer,

  applyMiddleware(thunk)

);

createStore其实可以接受三个参数,第二个参数preloadedState一般作为整个应用的初始化数据,如果传入了这个参数,applyMiddleware就会被当做第三个参数处理

[JavaScript] 纯文本查看 复制代码
const store = createStore(

  reducer,

  initialState,

  applyMiddleware(thunk)

);


中间件都要放到applyMiddleware里,如果要添加中间件,可以依次添加,但是要遵循文档定义的顺序

[JavaScript] 纯文本查看 复制代码
const store = createStore(

  reducer,

  initialState,

  applyMiddleware(thunk,middleware1, middleware2)

);


源码解读
也许你会奇怪,为什么使用的时候要按照上面的写法,那我们就一起看下方法的实现
[JavaScript] 纯文本查看 复制代码
首先是createStore的参数顺序

function createStore(reducer, preloadedState, enhancer) {

  var _ref2;



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

  }



  if (typeof reducer !== 'function') {

    throw new Error('Expected the reducer to be a function.');

  }


第一个判断已经告诉了我们答案,参数的类型检验结果决定了顺序

applyMiddleware是干什么用的
[JavaScript] 纯文本查看 复制代码
function applyMiddleware() {

  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {

    middlewares[_key] = arguments[_key];

  }



  return function (createStore) {

    return function () {

      for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {

        args[_key2] = arguments[_key2];

      }



      var store = createStore.apply(undefined, args);

      var _dispatch = function dispatch() {

        throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');

      };



      var middlewareAPI = {

        getState: store.getState,

        dispatch: function dispatch() {

          return _dispatch.apply(undefined, arguments);

        }

      };

      var chain = middlewares.map(function (middleware) {

        return middleware(middlewareAPI);

      });

      _dispatch = compose.apply(undefined, chain)(store.dispatch);



      return _extends({}, store, {

        dispatch: _dispatch

      });

    };

  };

}


代码不多,而且非常清晰:
1、applyMiddleware顾名思义,用于调用各种中间件;
2、applyMiddleware执行后,将所有入参中间件存入一个数组,并且返回一个闭包(闭包的概念不做累述)
3、闭包接受一个createStore作为入参并且执行后返回下一个闭包,createStore这个入参有没有很眼熟,没错,就是redux的createStore。

返回结果
返回将所有中间件串联存入的dispatch,执行时从右向左执行,第一次的执行结果会返回给一下个,依次类推。

如何实现每个中间件串联执行
_dispatch = compose.apply(undefined, chain),使用了一个compose函数,调用之后就可以将所有中间件串联起来,那么compose又是如何实现的呢?

精华所在
[JavaScript] 纯文本查看 复制代码
function compose() {

  for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {

    funcs[_key] = arguments[_key];

  }



  if (funcs.length === 0) {

    return function (arg) {

      return arg;

    };

  }



  if (funcs.length === 1) {

    return funcs[0];

  }



  return funcs.reduce(function (a, b) {

    return function () {

      return a(b.apply(undefined, arguments));

    };

  });

}


个人认为这个compose函数是整个redux中非常亮眼的部分,短短几行代码,就完成了一个核心功能的扩展,是责任链设计模式的经典体现。

ALSO 我们也可以使用这个compose方法对applyMiddleware进行扩展

[JavaScript] 纯文本查看 复制代码
let devtools = () => noop => {

          console.log(noop);

          return noop;   //createStore

      };

const enhancers = [

    applyMiddleware(...middleware),

    devtools()

  ];

createStore(reducers, initialState, compose(...enhancers));


然后回来,我们就明白了createStore中的设计
[JavaScript] 纯文本查看 复制代码
//如果存在中间件参数,那么将会得到一个经过改装的dispatch

// return _extends({}, store, {dispatch: _dispatch});

if (typeof enhancer !== 'undefined') {

    if (typeof enhancer !== 'function') {

      throw new Error('Expected the enhancer to be a function.');

    }



    return enhancer(createStore)(reducer, preloadedState);

  }

dispatch经过了怎样的改装
如上已经说过,compose会将传入的函数数组从右向左串联执行
[JavaScript] 纯文本查看 复制代码
compose.apply(undefined, chain)(store.dispatch);

thunk一定会接受上一个中间件的执行结果继续执行,然后最终在createState里返回一个改造好的dispatch, 接下来我只要看下thunk是怎样实现的,就了解了整个中间件使用的原理:
[JavaScript] 纯文本查看 复制代码


function createThunkMiddleware(extraArgument) {

  return function (_ref) {

    var dispatch = _ref.dispatch,

        getState = _ref.getState;

    return function (next) {

	        //最终的dispatch

	        //next就是接收的store.dispatch参数,为上一个中间件改造过的dispatch

      return function (action) {

        if (typeof action === 'function') {

          return action(dispatch, getState, extraArgument);

        }



        return next(action);

      };

    };

  };

}



var thunk = createThunkMiddleware();

thunk.withExtraArgument = createThunkMiddleware;



export default thunk;


代码同样精炼,改造后的dispatch入参接受的数据类型:
1、非function,不处理,将action 传给下一个中间件,最终都会根据传入的action计算相应的reducers(开头说的自执行)————store.dispatch(action)
2、function类型的action, 自动触发函数,并且将store.dispatch传入

总结
再结合开始介绍的thunk用法,我们就明白了thunk的原理,可以在actionCreators里通过返回一个函数,然后就可以在函数里编写某些异步操作了,待异步操作结束,最后通过传入的store.dispatch,发出action通知给Store要进行状态更新。

原文:https://blog.csdn.net/weixin_38642331/article/details/81748312
版权声明:本文为博主原创文章,转载请附上博文链接!

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马