手動實現簡易版 react-redux

實現簡易版 react-redux

上一節我們實現了簡易版的 redux,本節主要實現 react-redux。

同時解決下面問題:

  • 相比 redux, react-redux 有什麼好處/做了什麼
  • react-redux 用法

react-redux 有什麼好處

redux中的 createStore 方法返回三個內置方法getState, dispatch, subscribe。 每次獲取屬性都需要 store.getState,更新需要 dispatch。

在組件中, 我們想將state和 dispatch方法映射到props上,這樣取值就變得非常簡單,而 react-redux 就實現了這一層。

react-redux 實現

image.png

如上圖,通過 mapStateToProps方法將 state 轉爲可以傳遞的 props
dispatch方法有兩種形式,obj和function,通過不同的方法來實現將 dispatch方法轉爲 props
接下來,依次來實現這些方法

Provider

// 創建context
const ValueContext = React.createContext();

export class Provider extends Component {
  render() {
    return (
      <ValueContext.Provider value={this.props.store}>
        {this.props.children}
      </ValueContext.Provider>
    );
  }
}


bindActionCreators

作爲獨立的函數,先說下它的實現。其作用主要是將dispatch對象遍歷,取到每個action方法並映射到props

// {
//     add: () => ({type: "ADD"})
//   }
export function bindActionCreators(creators, dispatch) {
  const obj = {};
  for (const key in creators) {
    obj[key] = bindActionCreator(creators[key], dispatch);
  }
  return obj;
}

// bindActionCreator(add, dispatch) 轉換成 add => dispatch(add({type: "ADD"}))
function bindActionCreator(creator, dispatch) {
  return (...args) => dispatch(creator(...args));
}

connect

經過上面步驟,再看connect 函數就簡單許多:

export const connect = (
  mapStateToProps = state => state,
  mapDispatchToProps
) => WrappedComponent => {
  return class extends Component {
    // 此時組件的所有生命週期都能獲得this.context
    static contextType = ValueContext;
    constructor(props) {
      super(props);
      this.state = {
        props: {}
      };
    }
    componentDidMount() {
      const {subscribe} = this.context;
      this.update();
      // 訂閱
      subscribe(() => {
        this.update();
      });
    }

    update = () => {
      const {getState, dispatch, subscribe} = this.context;
      //  getState獲取當前store的state
      let stateProps = mapStateToProps(getState());
      let dispatchProps;
      // mapDispatchToProps Object/Function
      if (typeof mapDispatchToProps === "object") {
        dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
      } else if (typeof mapDispatchToProps === "function") {
        dispatchProps = mapDispatchToProps(dispatch, this.props);
      } else {
        // 默認
        dispatchProps = {dispatch};
      }
      this.setState({
        props: {
          ...stateProps,
          ...dispatchProps
        }
      });
    };
    render() {
      console.log("this.context", this.context); //sy-log
      return <WrappedComponent {...this.state.props} />;
    }
  };
};

如何使用

以常見的路由守衛舉例,調用 connect

// PrivateRoutePage.js
export default connect(
  // mapStateToProps
  ({user}) => ({isLogin: user.isLogin})
)(
  class PrivateRoute extends Component {
    render() {
      const {isLogin, path, component} = this.props;
      if (isLogin) {
        // 登錄
        return <Route path={path} component={component} />;
      } else {
        // 去登錄,跳轉登錄頁面
        return <Redirect to={{pathname: "/login", state: {redirect: path}}} />;
      }
    }
  }
);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章