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。
在这里插入图片描述
实现完毕!

猜你喜欢

转载自blog.csdn.net/lin_fightin/article/details/121067758