手写react9-context

176 阅读1分钟

使用context:

供应商:

let ThememContext = React.createContext();
class Page extends React.Component{
  constructor(props){
    super(props);
    this.state = {color:'red'};
  }
  changeColor = (color)=>{
    this.setState({color});
  }
  render(){
    let value = {color:this.state.color,changeColor:this.changeColor}
    return (
      <ThememContext.Provider value={value}>
        <div style={{margin:'10px',border:`5px solid ${this.state.color}`,padding:'5px',width:'250px'}}>
          page
          <Header/>
          <Main/>
        </div>
      </ThememContext.Provider>

消费分两种形式:

类组件的消费,可以定义一个静态属性:

class Header extends React.Component{
  static contextType = ThememContext
  render(){
    return (
      <div style={{margin:'10px',border:`5px solid ${this.context.color}`,padding:'5px'}}>
        header
        <Title/>  
      </div>
    )
  }
}

函数组件的消费,可以用配套的Consumer组件:

function Header(){
  return (
    <ThememContext.Consumer>
      {
        value=>(
          <div style={{margin:'10px',border:`5px solid ${value.color}`,padding:'5px'}}>
           header
           <Title/>  
          </div>
        )
      }
    </ThememContext.Consumer>
  )
}

添加常量:

//提供点
export const REACT_PROVIDER = Symbol('react.provider');
//消息者 
export const REACT_CONTEXT = Symbol('react.context');

通过React.createContext()创建出的context对象的结构:

{
  $$typeof: Symbol(react.context),
  Consumer: {$$typeof: Symbol(react.context), _context: context}
  Provider: {$$typeof: Symbol(react.provider), _context: context}
  _currentValue: {color:this.state.color,changeColor:this.changeColor}
}

React.createContext方法的实现:

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;
}

createDOM中兼容Provider和Consumer两种类型:

export function createDOM(vdom) {
    if (!vdom) return null;
    let { type, props, ref } = vdom;
    let dom;//真实DOM
    if(type && type.$$typeof === REACT_PROVIDER){
        return mountProvider(vdom);
    }else if(type && type.$$typeof === REACT_CONTEXT){
        return mountContext(vdom);
    }
function mountProvider(vdom){
    let { type, props, ref } = vdom;
    let context = type._context;
    context._currentValue = props.value;
    let renderVdom = props.children;
    vdom.oldRenderVdom = renderVdom;//这个操作就是让当前的虚拟DOM的oldRenderVdom指向要渲染的虚拟DOm
    return createDOM(renderVdom);
}
function mountContext(vdom){
    let { type, props, ref } = vdom;
    let context = type._context;
    let currentValue = context._currentValue;
    let renderVdom =props.children(currentValue);
    vdom.oldRenderVdom = renderVdom;//这个操作就是让当前的虚拟DOM的oldRenderVdom指向要渲染的虚拟DOm
    return createDOM(renderVdom);
}

mountProvider、mountContext中会调用createDOM,createDOM递归再调用mountClassComponent时,需要给类实例添加context属性,为类的方法提供context的值

function mountClassComponent(vdom) {
    let { type: ClassComponent, props, ref } = vdom;
    let classInstance = new ClassComponent(props);
    if(ClassComponent.contextType){
        classInstance.context = ClassComponent.contextType._currentValue;
    }

同时在每次更新时也要重新给context赋值:

class Component {
    static isReactComponent = true //当子类继承父类的时候 ,父类的静态属性也是可以继承的
    constructor(props) {
        this.props = props;
        this.state = {};
        this.updater = new Updater(this);
    }
    setState(partialState) {
        this.updater.addState(partialState);
    }
    //根据新的属性状态计算新的要渲染的虚拟DOM
    forceUpdate() {
        let oldRenderVdom = this.oldRenderVdom;//上一次类组件render方法计算得到的虚拟DOM
        //let oldDOM = oldRenderVdom.dom;
        let oldDOM = findDOM(oldRenderVdom);//获取 oldRenderVdom对应的真实DOM
        if(this.constructor.contextType){
            this.context = this.constructor.contextType._currentValue;
        }

在forceUpdate里面的compareTwoVdom,比较DOM、更新DOM时,也要在Provider中将context的值更新:

function updateElement(oldVdom, newVdom) {
    //Provider更新
    if(oldVdom.type.$$typeof === REACT_PROVIDER){
        updateProvider(oldVdom, newVdom);
    //Consumer的更新
    }else if(oldVdom.type.$$typeof === REACT_CONTEXT){
        updateContext(oldVdom, newVdom);
function updateProvider(oldVdom, newVdom){
    let currentDOM = findDOM(oldVdom);//<div style={{margin:'10px'
    let parentDOM = currentDOM.parentNode;//div#root
    let {type,props} = newVdom;//type ={$$typeof:REACT_PROVIDER,_context:context }
    let context = type._context;
    context._currentValue = props.value;//给context赋上新的_currentValue
    let renderVdom = props.children;
    compareTwoVdom(parentDOM,oldVdom.oldRenderVdom,renderVdom);
    newVdom.oldRenderVdom = renderVdom;
}
function updateContext(oldVdom, newVdom){
    let currentDOM = findDOM(oldVdom);//<div style={{margin:'10px'
    let parentDOM = currentDOM.parentNode;//div#root
    let {type,props} = newVdom;//type ={$$typeof:REACT_PROVIDER,_context:context }
    let context = type._context;
    let renderVdom = props.children(context._currentValue);
    compareTwoVdom(parentDOM,oldVdom.oldRenderVdom,renderVdom);
    newVdom.oldRenderVdom = renderVdom;
}