看不懂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在多个地方使用会不会有问题,之类的,但是我们不管了(😂)

以上,谢谢。

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