Skip to content

Redux

导航目录

Redux 是什么

  • Redux 是 React 状态管理工具库,可以实现跨组件通信,解决组件间状态共享的问题

Redux 设计思想

Redux 核心设计思想是发布-订阅模式,通过以下步骤实现:

  1. 调用 createStore 时,接收两个参数 reducerinitState,返回一个 store 实例
  2. 内部会初始化 state 和 listeners 收集器,向全局暴露出三个核心函数:getStatesubscribedispatch
  3. getState:用于外部获取最新的 state
  4. subscribe:订阅器,将订阅的回调函数存储在 listeners 收集器中,返回一个取消订阅的回调
  5. dispatch:派发器,执行 reducer 获取最新的 state,同时执行所有订阅器
js
export default function createStore(reducer, initState) {
  let state = initState;

  // 收集器
  const listeners = [];

  // 获取状态
  const getState = () => {
    return state;
  };

  /**
   *
   * @param {*} listener 订阅的函数
   * @returns 取消订阅的函数
   */
  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  };

  /**
   *
   * @param {*} action action提交计算新的状态
   */
  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach((listener) => listener());
  };

  dispatch({ type: "@@REDUX/INIT" });

  return {
    getState,
    subscribe,
    dispatch,
  };
}

combineReducers

默认情况下,我们只有一个 Reducer,如果项目比较庞大,所有的数据全部交给一个 Reducer 处理会比较冗余。combineReducers的目的就是将多个模块的 Reducer 进行合并成一个大的 Reducers,实现集中派发

缺陷:每次触发 dispatch,所有 reducer 都会执行一遍

js
/**
 * 合并多个reducers
 * @param {*} reducers
 */
export default function combineReducers(reducers) {
  /**
   * createStore执行dispatch触发
   * @param {*} state 老的state状态
   * @param {*} action action动作
   */
  return function combination(state = {}, action) {
    // 总的state
    let nextState = {};
    // TODO:缺陷每次触发dispatch,所有reducer都会执行一遍
    for (const key in reducers) {
      const preState = state[key]; // 老的state状态
      nextState[key] = reducers[key](preState, action);
    }
    return nextState;
  };
}

React-Redux

React-Redux 是用来连接组件和仓库的库,内部将订阅、解绑、视图更新统一封装,简化用户操作。它由两个核心部分组成:Providerconnect

Provider

Provider 组件基于 React 的 createContext 实现,其目的是将 store 实例存储在 Context 上下文对象中,使所有子组件都能访问到 store

js
import { createContext } from "react";
export const ReactReduxContext = createContext( null );
export default ReactReduxContext;

/**
 * 利用Context给子孙组件传递store实例
 * @param {*} props store实例
 * @returns
 */
export default function Provider ( props ) {
  return (
    <ReactReduxContext.Provider value={{ store: props.store }}>
      {props.children}
    </ReactReduxContext.Provider>
  )
}

connect

connect 是连接组件和仓库的核心,它是一个高阶组件,主要功能包括:

  • 从 Context 上下文解析出 store 实例
  • 自动进行订阅,当触发 Dispatch 时,会通知订阅器,获取最新的 state,触发视图更新
  • 将 props、state 传递给原始组件并返回增强后的组件
js
import { Component } from "react";
import ReactReduxContext from "./ReactReduxContext";
/**
 * 连接组件和仓库
 * @param {*} mapStateToProps
 * @param {*} mapDispatchToProps
 * @returns
 */
export default function connect(mapStateToProps, mapDispatchToProps) {
  return (OldComponent) => {
    return class extends Component {
      static contextType = ReactReduxContext; // 注入store实例
      constructor(props, context) {
        super(props);
        // 解析store实例
        let {
          store: { getState, subscribe, dispatch },
        } = context;
        this.state = mapStateToProps(getState());

        // 订阅,dispatch执行会触发订阅器,触发更新
        this.unsubscribe = subscribe(() => {
          this.setState(mapStateToProps(getState()));
        });
        let dispatchProps;
        if (typeof mapDispatchToProps === "function") {
          // 执行注入dispatch
          dispatchProps = mapDispatchToProps(dispatch);
        } else {
          dispatchProps = {};
          // 将action动作,转换成dispatch能触发的动作
          for (const key in mapDispatchToProps) {
            dispatchProps[key] = () => dispatch(mapDispatchToProps[key]());
          }
        }

        this.dispatchProps = dispatchProps;
      }

      // 组件销毁时取消订阅
      componentWillUnmount() {
        this.unsubscribe();
      }
      render() {
        return (
          <OldComponent
            {...this.props}
            {...this.state}
            {...this.dispatchProps}
          ></OldComponent>
        );
      }
    };
  };
}

applyMiddleware 中间件处理

applyMiddleware 是 Redux 中用于增强 dispatch 功能的机制,方便开发人员根据需求定制自己的插件。它是一个三层闭包函数:

  1. 第一层接收中间件数组
  2. 第二层接收 createStore 函数
  3. 最后一层接收总的 reducer 和初始的 initState

其工作原理:

  • 内部会创建 Store 实例
  • 创建 middlewareAPI 封存在中间件内部
  • 调用每一个中间件,形成调用栈
  • 最后返回 store 实例和增强后的 dispatch
js
// BUG:如果在中间件直接调用第一层的dispatch,会造成死循环
export default function logger({ getState, dispatch }) {
  return (nextDispatch) => {
    return (action) => {
      console.log("更新前", getState());
      nextDispatch(action);
      console.log("更新后", getState());
    };
  };
}
js
import compose from "./compose";

/**
 *
 * @param  {...any} middlewares 中间件
 */
export default function applyMiddleware(...middlewares) {
  return (createStore) => {
    return (reducer, initState) => {
      let dispatch;
      // 创建Store实例
      const store = createStore(reducer, initState);
      // 封存中间件API
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (action) => dispatch(action), // 加强后的dispatch
      };
      // 揭开中间第一层(封存原始的store)
      // BUG:如果在中间件直接调用第一层的dispatch,会造成死循环
      const chain = middlewares.map((middleware) => middleware(middlewareAPI));
      // 增强后的dispatch(揭开中间第二层)
      dispatch = compose(...chain)(store.dispatch);
      // 第三层用户去触发
      return { ...store, dispatch };
    };
  };
}