看不懂react-redux就寫一個,超精簡版

react-redux的代碼,還挺多的,偷懶不想看,但是又怕別人問原理,所以就手擼一個來玩玩,超精簡

慣例,先看react-redux的使用,這是一個按鈕加一個p標籤,初始化是0,點了以後隨機生成數字,很簡單,看不懂請看redux文檔

index.jsx文件

import ReactDOM from "react-dom";
import React from "react";
import { createStore } from "redux";

import { Provider } from "react-redux";
import Comp1 from "./comp1.jsx";

class App extends React.Component {
  render() {
    return (
      <div>
        <Comp1 />
      </div>
    );
  }
}

// 定義一個reducer
const reducer = (state = [], action) => {
  switch (action.type) {
    case "change_num":
      return { ...state, activeNum: action.activeNum };

    default:
      return state;
  }
};

// createStore
const store = createStore(reducer, {
  activeNum: 0
});

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

=================================================================

comp1.jsx文件

import React, { Component } from "react";
import { connect } from "react-redux";

// 定義action
const action = val => ({
  type: "change_num",
  activeNum: val
});

class Comp1 extends Component {
  render() {
    const { activeNum, changeNum } = this.props;
    return (
      <div>
        <p>{activeNum}</p>
        <button onClick={() => changeNum(Math.random())}>click</button>
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => ({
  activeNum: state.activeNum
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  changeNum: val => {
    dispatch(action(val));
  }
});

// 調用connect
export default connect(mapStateToProps, mapDispatchToProps)(Comp1);

我們關注下react-redux做了什麼事情,一個是connect,一個是Provider,沒了。

我們都知道redux是發佈訂閱,那麼react-redux就是要把這套發佈訂閱用起來,因爲我們都看過react-redux了,知道他用了react的Context,那麼毫無疑問store在Provider傳入之後,是作爲Context.Provider的value,不然讓人家怎麼發佈和訂閱呢,於是我們yy了一下Provider的代碼

// 創建一個Context肯定是要的
const Context = React.createContext("hehe");

export class Provider extends React.Component {
  render() {
    return (
      // 不管怎樣反正先把store給了value再說
      <Context.Provider value={this.props.store}>
        {this.props.children}
      </Context.Provider>
    );
  }
}

這樣寫我們至少把使用Provider的樣子寫出來了對吧,貼一下方便回憶

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

然後就是connect了,我們看到它是一個高階函數,傳了兩個mapxxx函數和組件,那麼至少我們寫的connect是個套了三層(兩層?就這個意思)的函數,並且最裏面一層返回了一個組件,這個組件訂閱了store的變更,並且把store最新的值放到了被connect的Comp1上,我們yy一下這個組件的代碼

import React from "react";

class Hoc extends React.Component {
  constructor(props) {
    super(props);
    // 這個state存在的意義就是下面訂閱的時候觸發更新,當然,是沒有性能優化的,誰叫我們是超精簡
    this.state = {};
  }

  componentDidMount() {
    // 這裏我們訂閱了store的變更,這個props上的屬性是怎麼來的我們馬上就知道了
    this.props.store.subscribe(() => {
      // 這裏不觸發一下更新的話,store.getState()是不會變的
      this.setState({});
    });
  }

  render() {
    // 這裏我們把兩個mapxxx函數調用了一把,爲什麼這麼調用,因爲兩個mapxxx函數就長了一副應該這麼調用的臉
    const { comp: Comp, mapState, mapDispath, store } = this.props;
    const state = mapState(store.getState());
    const dispatch = mapDispath(store.dispatch);
    return <Comp {...state} {...dispatch} />;
  }
}

上面的註釋說得不太清楚,我們來看看兩個mapxxx函數,mapStateToProps需要傳入一個state對象,返回一個對象,裏面是一些屬性,讓Comp1可以在props上拿到,mapDispatchToProps需要傳入一個dispatch函數,返回一個對象,裏面是一些函數,讓Comp1可以調用,那麼顯然一個就是store的getState返回值,一個是store的dispatch方法了(應該還挺顯然的吧?),不顯然也沒事,我們先這麼寫着

const mapStateToProps = (state, ownProps) => ({
  activeNum: state.activeNum
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  changeNum: val => {
    dispatch(action(val));
  }
});

這樣的話connect長啥樣我們也推導出來了,就像這樣

// 第一層,接收mapxxx
export function connect(mapState, mapDispath) {
  // 第二層,接收組件
  return function(comp) {
    // 終於到了Consumer的時候了
    return function() {
      return (
        <Context.Consumer>
          {store => (
            <Hoc
              comp={comp}
              store={store}
              mapState={mapState}
              mapDispath={mapDispath}
            />
          )}
        </Context.Consumer>
      );
    };
  };
}

好了,到這裏,把上面寫的這些代碼片段合起來,創建一個文件叫做hello-redux.jsx,並把我們頂部例子裏的react-redux替換掉,就可以跑了,大家可以試一試

當然,因爲是精簡版,所以問題很多,比如mapxxx裏有個ownProps的參數我們就沒給,那個叫hehe的Context在多個地方使用會不會有問題,之類的,但是我們不管了(😂)

以上,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章