ZF_react redux 中间件thunk promise logger applyMiddleware 中间件级联实现

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZF_react redux 中间件thunk promise logger applyMiddleware 中间件级联实现相关的知识,希望对你有一定的参考价值。

redux设计思想

Redux是将整个应用状态存储到一个地方,称为store。
里面保存一颗状态数state tree。
组件可以派发dispatch行为action给store,而不是直接通知其他组件。
其他组件可以通过订阅store中的状态来刷新视图。

Redux三大原则:

  • 整个应用的state被存储在一颗object tree中,并且这个object tree只存在于唯一一个store.
  • state是只读的,唯一可以改变state的方法就是触发action.action是一个用于描述已发生事件的普通对象,使用纯函数来执行修改,为了描述action如何改变state tree,你需要编写reducer(reducer规范新的state创建)
  • 单一数据源的设计让React组件之间的通信更加方便,同时也便于状态的统一管理。

概念

* store,状态管理库,可以当成一个仓库
* state 状态,可以当作仓库里的东西
* action 动作对象,比如要存放东西到仓库,从仓库取出东西等等。
* dispatch 派发 , 比如你想存放东西到仓库,你创建了这个动作对象,你就需要告诉store。你需要执行这个动作对象,存放东西
* reducer 规范新的state的创建流程。比如你dispatch一个action后,store就会调用reduer,将现有仓库里的东西(state)以及你要做的动作(action)告诉reducer,reducer就会通过action判断你要做什么,从而返回新的state。比如存放东西,一开始state是1,后来存放后state变为2.reducer就会返回新的state 2。更新s状态管理库的东西(store中的state)
* subcribe监听,当你dispatch一个动作对象之后,你希望store在更新完仓库里的东西之后,第一时间通知你。就可以通过subcribe进行订阅。就像直播一样,你订阅一个主播,他直播了就会通知你。store也会在第一时间通知你state改变了。

这里有篇简单实现redux

bindActionCreate的实现

将action与store一起绑定。
用法:

每次dispatch的时候都需要调用store.dispatch,比较麻烦。

使用bindActionCreateors可以将store.dispatch与action连接起来。
具体实现:
直接遍历传入的第一个参数对象,将每个值都处理,使用dispatch关联起来,再返回。
可以正常使用。

combineReducer的实现

用来合并reducer的东西。因为store只有一个,很多页面如果都用一个reducer的话,会太多,所以一般将reducer划分开来,然后再合并。conbineReducer就是来实现合并每个页面的reducer的
使用:

使用combineReducers后,state会变成一个对象。combineRedcuers依然返回的是一个函数,接受state和action。
思路:
采用柯里化存储需要合并的reducers。然后重新写一个大型reducers,他依然接受state和action,但他会遍历所有的需要合并的reducers并执行,获取所有的值后合并成一个对象返回。

如上,保存了reducers,重写了返回的函数,依然接受state和action,每次都遍历执行reducers,返回新的对象。
看效果:


两个状态互不影响,这就实现了combineReducers。所有的reducers都会走一遍。

react-redux

用来连接react跟redux,让其用法更加简洁。
Provider, connect等,具体可以了解redux

import React, { useContext, useLayoutEffect, useState, useMemo } from "react";
import { bindActionCreators } from "../redux";
import { ReactReduxContext } from "./";

/**
 * 状态变化监听,让组件刷新
 * @param {*} mapState 状态映射属性
 * @param {*} mapDispatch  action映射属性
 * @returns
 */
const connect = (mapState, mapDispatch) => (OldCom) => {
  return function (props) {
    const {
      store: { getState, dispatch, subscribe, listeners },
    } = useContext(ReactReduxContext);

    //把状态映射成属性
    const preState = getState();
    const stateProps = useMemo(() => mapState(preState), [preState]);

    //把action映射成属性
    let dispatchProps = useMemo(() => {
      if (typeof mapDispatch === "function") {
        return mapDispatch(dispatch);
      } else if (typeof mapDispatch === "object") {
        //对象就直接绑定
        return bindActionCreators(mapDispatch);
      } else {
        //都不是直接把dispatch给他
        return { dispatch };
      }
    }, []);

    //模拟类组件的强制刷新,类组件就直接this.setState了
    const [, forceUpdate] = useState();

    //订阅状态
    useLayoutEffect(() => {
      //每次变化强制更新组件
      const unSubscribe = subscribe((newState) => {
        const newStateProps = mapState(newState);
        //精准渲染
        for (let key in stateProps) {
          if (stateProps[key] !== newStateProps[key]) {
            //触发更新之后,会重新渲染组件,重新执行connect,外部的statePorps得到更新。
            forceUpdate({});
            break;
          }
        }
      });
      //记得销毁
      return unSubscribe;
    }, [stateProps]);

    return <OldCom {...stateProps} {...dispatchProps} {...props} />;
  };
};

export default connect;

import React from "react";

import { ReactReduxContext } from "./";

const Provider = (props) => {
  const { store, children, ...otherProps } = props;

  return (
    <ReactReduxContext.Provider value={{store}} {...otherProps}>
      {children}
    </ReactReduxContext.Provider>
  );
};


export default Provider

hooks版的实现

useSelector useDispatch

useDispatch直接返回dispatch就行。
useSelector比较麻烦,他跟connect一样,也是用来连接组件和redux的。
接受两个参数,第一个类似于connect的mapStateToProps,第二个参数是一个比对函数。
实现思路:借鉴connect的思想,获取最新的state传入第一个参数,拿到组件想要的props。然后通过UseLayoutEffect来订阅数据的改变,通过比对函数来判断是否要更新。

可以看到跟conncect的实现思路差不多。
在模拟react-redux的shallowEqual
它是用来判断当前组件以来的state是否改变,如果没改变不会刷新,从而优化性能。



正常运行,并且精准渲染。

compose的实现

compose是用来组合函数返回一个新函数。

const fn = compose(a,b,c)
fn(2)


更加简洁的写法:

第一次执行的时候返回一个函数,到最后一个也是返回一个函数,传入的值比如传一个3,如是compose(a,b,c),fn(3 ) => 会先c(3)再b(c(3))再a(b(c(3)))这样获取最终的结果。最终compose返回的是应该是(…args)=>c(b(a(…args))),就像中间件一样,计算后的值一层一层传下去。

中间件

我们现在的redux是dispatch->action->reducer->newState。是同步的,而有时候我们需要异步获取数据,数据获取后再去触发reducer修改state。这时候工作流程就变成action -middlewares-reducer。

先触发发送请求的action,有中间件的话就通过middleware,然后拿到数据之后再触发修改的action,去修改state。

  • 中间件的核心原理,就是重写dispatch,在原始的dispatch之前之后,书写自己的逻辑。
    最简单的改造
const dispatch = store.dispatch //先存放原始的dispatch
store.dispatch = (action)=> {
//重新指向
	setTimeout(()=>{
	dispatch(action)
},2000)
}

dispatch被我们重写之后,就支持异步了,它会在两秒后再去触发action。

实现dispatch前后打印state。

先实现一个中间件。

logger就是一个中间件,他获取store的getState和dispatch。

使用:

是不是跟我们平常用的有些出入,这只是一个中间件的写法。

redux-thunk的实现

让dispatch支持函数action.

/**
 * 中间件的写法是固定的
 * 接受的参数里面的属性分别是getState, dispatch
 */
function thunk({ getState, dispatch }) {
  return function (next) {
    //next实现中间件的级联,调用下一个中间件
    return function (action) {
      if (typeof action === "function") {
          //函数的话,执行并且输入参数
        return action(dispatch, getState);
      }
      return  next(action);
    };
  };
}


如果是函数的话,

const action = (data) => (dispatch, getState) => {
			dispatch({...})
}

dispatch(action(123))

如:



会在两秒后才执行。

redux-promise的实现

/**
 * 中间件的写法是固定的
 * 接受的参数里面的属性分别是getState, dispatch
 * action有两种,一种是action.payload为Promise,一种是直接返回一个Promise
 */
function promise({ getState, dispatch }) {
  return function (next) {
    //next实现中间件的级联,调用下一个中间件
    return function (action) {
      if (action.payload && action.payload instanceof Promise) {
        action.payload
          .then((res) => dispatch({ ...action, payload: res }))
          .catch((error) => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          });
      } else if (action instanceof Promise) {
        action.then(dispatch).reject(dispatch);
      }
      return next(action);
    };
  };
}

export default promise;

promise的action有两种写法,所以做两个判断。思路跟thunk是一样的。
如:



两s后渲染

直接返回一个promise也行。

实现中间件级联

现在我们的中间件是单独用的,借助compose将他们级联起来。

这层compose(…middle)(store.dispatch)就是关键实现,他最终返回的是一个dispatch,该dispatch接受promise,参数next就是下一个中间件thunk的dispatch,以此类推。

如图,dispatch的流程,从promise->thunk->logger->store.dispatch

效果:


触发promise

先走promise,.then之后再继续往下走。可以看到顺序是一样的。
现在级联就实现完毕了。但是跟真正的redux的写法有点出入,让我们改造一下。

改造redux实现官网的效果


这样写。

enhancer就是applyMiddleware([xxx]),跟我们第一种写法一摸一样。

又走到createStore,但是此时没有enhancer,所以直接创建了store。

实现完毕!

以上是关于ZF_react redux 中间件thunk promise logger applyMiddleware 中间件级联实现的主要内容,如果未能解决你的问题,请参考以下文章

redux的中间件之redux-thunk

redux的中间件之redux-thunk

redux的中间件之redux-thunk

redux中间件和redux-thunk实现原理

redux 中的 redux-thunk(中间件)

无法理解 Redux-thunk 中间件