[React Redux 源码] 来聊聊 React Redux

139 阅读2分钟

React Redux

💡 React 使用 redux 的时候,每一个类组件,都要在生命周期当中去订阅,取消订阅,去绑定dispatch,获取状态。所以可以使用 HOC 统一一优化,React-redux 当中提供了一个 connect 高阶函数和 Provider组件 来优化这些统一的操作。提供 useSelector, useDispatch Hook 来供函数式组件消费 store 当中的数据。

实现 React Redux

Provider 组件

💡 将 redux 当中的 store,通过 React Context 全局共享。

export const ReactReduxContext = React.createContext(null);

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

Connect 函数

💡 通过向 connect 函数 当中传递 mapStateToProps | mapDispatchToProps, 将 redux 中的 storedispatch 通过 HOC 映射到 组件的 props 当中。

类组件版

function connect(mapStateToProps, mapDispatchToProps) {
  return function (OldComponent) {
    return class extends React.Component {
      static contextType = ReactReduxContext;
      constructor(props, context) {
        super(props);
        const { store } = context;
        const { getState, subscribe, dispatch } = store;
        this.state = mapStateToProps(getState());
        this.unsubscribe = subscribe(() => {
          this.setState(mapStateToProps(getState()));
        });
        let dispatchProps;
        //把dispatch映射为属性
        if (typeof mapDispatchToProps === "function") {
          dispatchProps = mapDispatchToProps(dispatch);
        } else if (typeof mapDispatchToProps === "object") {
          dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
        } else {
          dispatchProps = { dispatch };
        }
        this.dispatchProps = dispatchProps;
      }
      componentWillUnmount() {
        this.unsubscribe();
      }
      render() {
        return (
          <OldComponent
            {...this.props}
            {...this.state}
            {...this.dispatchProps}
          />
        );
      }
    };
  };
}

函数组件版

// hook 实现 connect 函数
function connect2(mapStateToProps, mapDispatchToProps) {
  return function (OldComponent) {
    return function (props) {
      //返回的Counter1组件
      const { store } = useContext(ReactReduxContext);
      const { getState, dispatch, subscribe } = store;
      const prevState = getState();
      //把状态映射为属性
      const stateProps = useMemo(() => mapStateToProps(prevState), [prevState]);
      let dispatchProps = useMemo(() => {
        console.log("dispatchProps render");
        let dispatchProps;
        if (typeof mapDispatchToProps === "function") {
          dispatchProps = mapDispatchToProps(dispatch);
        } else if (typeof mapDispatchToProps === "object") {
          dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
        } else {
          dispatchProps = { dispatch };
        }
        return dispatchProps;
      }, [dispatch]);
      //把dispatch映射为属性

      //forceUpdate模拟的类组件的强制刷新方法 React 18之后可以新的API  useSyncExternalStore
      const [, forceUpdate] = useReducer((x) => x + 1, 0);
      useLayoutEffect(() => {
        //如果仓库里的状态发生变化之后,会就执行forceUpdate
        return subscribe(forceUpdate);
      }, [subscribe]);
      return <OldComponent {...props} {...stateProps} {...dispatchProps} />;
    };
  };
}

useSelector Hook

通过 context 拿到共享的 store,通过传递的 selector 来计算 新的 state 值。

function useSelectorWithStore(selector, store) {
  let { subscribe, getState } = store;
  const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
  let state = getState();
  let selectedState = selector(state);
  React.useLayoutEffect(() => {
    //其实这个订阅只会执行一次就可以了
    return subscribe(forceUpdate);
  }, [subscribe]);
  return selectedState;
}

function useSelector(selector) {
  const { store } = React.useContext(ReactReduxContext);
  const selectedState = useSelectorWithStore(selector, store);
  return selectedState;
}

useDispatch Hook

通过 context 拿到共享的 dispatch

function useDispatch() {
  const { store } = React.useContext(ReactReduxContext);
  return store.dispatch;
}