react-redux源码剖析

1,074 阅读2分钟

顾名思义react-redux是对于redux的延伸以及功能的扩展具体,那我们先来说说redux目前存在的问题

redux目前存在的问题

1、每个组件都需要进行订阅并且进行强制更新操作
2、如果要获取store状态或者执行dispatch就需要在组件内引入store文件,非常繁琐以及不好维护

react-redux提供了两个API

1、Provider 为后代组件提供store
2、Connect 为组件提供数据和变更方法

react-redux的使用

// index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import store from "./store/";
import {Provider} from "react-redux";
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

1、在类组件中的使用

//组件中的应用
import React, {Component} from "react";
import {connect} from "react-redux";

@connect(
  // mapStateToProps 把state放到props上一份
    state => {
      return {count: state.count};
    },
  // mapDispatchToProps object|function 把dispatch放到props上一份
    {
      add: () => ({type: "ADD"}),
      minus: () => ({type: "MINUS"})
    }
)
class ReactReduxPage extends Component {
  render() {
    const {count, dispatch, add, minus} = this.props;
    return (
      <div>
        <h3>ReactReduxPage</h3>
        <p>{count}</p>
        <button onClick={() => dispatch({type: "ADD", payload: 100})}>
          dispatch add
        </button>
        <button onClick={add}> add</button>
        <button onClick={minus}> minus</button>
      </div>
    );
  }
}
export default ReactReduxPage;

2、在函数组件中的使用

import React, {useCallback} from "react";
import {useSelector, useDispatch} from "react-redux";

export default function ReactReduxHookPage(props) {
  const count = useSelector(({count}) => count);
  const dispatch = useDispatch();
  const add = useCallback(() => {
    dispatch({type: "ADD"});
  }, []);
  return (
    <div>
      <h3>ReactReduxHookPage</h3>
      <p>{count}</p>
      <button onClick={add}>add</button>
    </div>
  );
}

从上面的不同类型组件中的使用可以看出react-redux即提供了在类组件中的mapStateToProps以及mapDispatchToProps,还有函数组件中的两个钩子函数useSelector以及useDispatch

针对类组件的源码剖析

import React, {useContext, useReducer, useEffect, useLayoutEffect} from "react";

const Context = React.createContext();
export const connect = (
  mapStateToProps = state => state,
  mapDispatchToProps
) => WrappedComponent => props => {
  //props是WrappedComponent的属性值
  const store = useContext(Context);
  const {getState, dispatch, subscribe} = store;
  // todo 获取state
  const stateProps = mapStateToProps(getState());
  let dispatchProps = {
    dispatch
  };

  if (typeof mapDispatchToProps === "function") {
    dispatchProps = mapDispatchToProps(dispatch);
  } else if (typeof mapDispatchToProps === "object") {
    dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
  }

  // 函数组件实现forceUpdate的方法
  const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

  useLayoutEffect(() => {
    const unsubscribe = subscribe(() => {
      forceUpdate();
    });
    return () => {
      if (unsubscribe) {
        unsubscribe();
      }
    };
  }, [store]);

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

export function Provider({children, store}) {
  return <Context.Provider value={store}>{children}</Context.Provider>;
}

针对函数组件的源码剖析

export function Provider({children, store}) {
  return <Context.Provider value={store}>{children}</Context.Provider>;
}

function bindActionCreator(creator, dispatch) {
  return (...arg) => dispatch(creator(...arg));
}

export function bindActionCreators(creators, dispatch) {
  let obj = {};
  for (let key in creators) {
    obj[key] = bindActionCreator(creators[key], dispatch);
  }
  return obj;
}

export function useSelector(selector) {
  const store = useStore();
  const {getState, subscribe} = store;
  const selectState = selector(getState());
  const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

  useLayoutEffect(() => {
    const unsubscribe = subscribe(() => {
      forceUpdate();
    });
    return () => {
      if (unsubscribe) {
        unsubscribe();
      }
    };
  }, [store]);

  return selectState;
}

export function useDispatch() {
  const store = useStore();
  return store.dispatch;
}

function useStore() {
  const store = useContext(Context);
  return store;
}