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