手写redux2-react-redux原理实现

125 阅读2分钟
import React from 'react';
import ReactDOM from 'react-dom';
import Counter1 from './components/Counter1';
import Counter2 from './components/Counter2';
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
    <Counter1 />
    <Counter2 />
</Provider>, document.getElementById('root'));
import React, { Component } from 'react'
import {connect} from 'react-redux';
import actions from '../store/actions/counter1';
//Counter1.props === {...state.counter1,...actions};
class Counter1 extends Component {
    render() {
        let {color,number,add1,minus1,changeColor} = this.props;
        return (
            <div style={{color}}>
                <p>{number}</p>
                <button onClick={add1}>+</button>
                <button onClick={minus1}>-</button>
                <button onClick={()=>changeColor('red')}>改成红色</button>
            </div>
        )
    }
}
//把仓库中的状态映射为组件的属性对象 输入
let mapStateToProps = state=>state.counter1;
//把派发的动作映射为属性对象
//let mapDispatchToProps
export default connect(
    mapStateToProps,
    actions
)(Counter1);

react-redux的作用就是将state和action都映射到组件属性上

还有一种写法:

import React, { Component } from 'react'
import {connect} from '../react-redux';
import actions from '../store/actions/counter1';
import * as actionTypes from '../store/action-types';

class Counter1 extends Component {
    render() {
        let {color,number,add1,minus1,changeColor} = this.props;
        return (
            <div style={{color}}>
                <p>{number}</p>
                <button onClick={add1}>+</button>
                <button onClick={minus1}>-</button>
                <button onClick={()=>changeColor('red')}>改成红色</button>
            </div>
        )
    }
}
//把仓库中的状态映射为组件的属性对象 输入
let mapStateToProps = state=>state.counter1;
//把dispatch方法映射为一个属性对象 
let mapDispatchToProps = dispath=>({
    add1(){
        dispath({type:actionTypes.ADD1});
    },
    minus1(){
        dispath({type:actionTypes.MINUS1});
    },
    changeColor(color){
        dispath({type:actionTypes.CHANGE_COLOR,payload:color});
    }
})
//把派发的动作映射为属性对象
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter1);

如果是函数组件,还可以使用useSelector和useDispatch两个hooks,分别对应mapStateToProps、mapDispatchToProps

import React from 'react'
import {useSelector,useDispatch} from '../react-redux';
import actions from '../store/actions/counter2';
let mapStateToProps = state=>state.counter2;
function Counter2(props){
    //在函数组件的中可以使用useSelector替换掉mapStateToProps
    let counter2 = useSelector(mapStateToProps);
    let dispatch = useDispatch();
    const {number,color}=counter2;
    return (
        <div style={{color}}>
            <p>{number}</p>
            <button onClick={()=>dispatch(actions.add2())}>+</button>
            <button onClick={()=>dispatch(actions.minus2())}>-</button>
            <button onClick={()=>dispatch(actions.changeColor('green'))}>改成绿色</button>
        </div>
    )
}
export default Counter2;

上述代码的执行流程分析:

1、当执行到import Counter1 from './components/Counter1'; 这一行时,会进入Counter1组件中,继续解析import {connect} from'../react-redux';,得到connect方法之后,会立刻执行(即将Counter1用高阶组件包装再作为导出组件的定义):

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter1);

2、接下来执行到了import { Provider } from 'react-redux';这一行,沿着它的import引用,我们可以追踪到:

export {default as Provider} from './Provider';
import ReactReduxContext from './ReactReduxContext';
export const ReactReduxContext = React.createContext(null);

所以这一行代码解析的时候,最终走到createContext,就会创建出一个Context对象:

function createContext(){
    let context = { $$typeof: REACT_CONTEXT,_currentValue:null};
    context.Provider = {
        $$typeof:REACT_PROVIDER,
        _context:context
    }
    context.Consumer = {
        $$typeof:REACT_CONTEXT,
        _context:context
    }
    return context;
}

3、接下来开始执行

ReactDOM.render(
<Provider store={store}>
    <Counter1 />
    <Counter3 />
</Provider>, document.getElementById('root'));

jsx编译之后为:

ReactDOM.render(
  React.createElement(Provider, {
    store: store
  }, React.createElement(Counter1, null), React.createElement(Counter3, null))
,document.getElementById('root'));

render 方法中,遇到的第一层vdom的type应该是Provider对应的函数组件,即应该走mountFunctionComponent这个分支

mountFunctionComponent会传入props作为参数并直接执行,返回的vdom结构应该是:

{
	type: ReactReduxContext.Provider,
  props: {
    value: {
      store: props.store
    }
  },
  children: props.children
}

紧接着,该vdom会再次传入createDOM方法中,然后再进入mountProvider分支中

这个分支里会将type._context._currentValue的值更新为props.value,在这里就是

type._context._currentValue === {
  store: props.store
}

再将children传入createDOM中,之后在children渲染时就可以拿到这里的store的值了

接下来就开始渲染Counter1 Counter3这些children了

注意,其本质上是渲染的经过connect包装的高阶组件