react源码分析/createContext
Posted web前端每日干货
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react源码分析/createContext相关的知识,希望对你有一定的参考价值。
createContext.js
先看一个demo
import React from 'react'
import styles from './index.css'
const ThemeContext = React.createContext('light')
console.log(ThemeContext)
export default class Test extends React.Component {
constructor(props) {
super(props)
this.state = { theme: 'light' }
}
toggleTheme = () => {
this.setState(({ theme }) => ({
theme: theme === 'light' ? 'dark' : 'light'
}))
}
render() {
return (
<React.Fragment>
<ThemeContext.Provider value={this.state.theme}>
<AppBody onClick={this.toggleTheme} />
<div />
</ThemeContext.Provider>
</React.Fragment>
)
}
}
class AppBody extends React.PureComponent {
render() {
console.log('AppBody rendered')
const { onClick } = this.props
return (
<ThemeContext.Consumer>
{theme => (
<button className={`${theme}`} onClick={onClick}>
{theme}
</button>
)}
</ThemeContext.Consumer>
)
}
}
按钮点击,butoon 的颜色发生变了。但是你获取奇怪。这里使用
react.PureComponent,组件本身会对props,state进行简单钱比较,然后决定要不要更新,即是shouldComponentUpdate 返回 false,不进行更新。但是点击按钮,按钮背景色却发生了变化。主要的原因就是React.createContext()
分析一下ReactContext.js
*ReactContext的类型
// 多renderer 并发工作
export type ReactContext<T> = {
$$typeof: Symbol | number,
Consumer: ReactContext<T>,
Provider: ReactProviderType<T>,
_calculateChangedBits: ((a: T, b: T) => number) | null,
_currentValue: T,
_currentValue2: T,
_threadCount: number,
// DEV only
_currentRenderer?: Object | null,
_currentRenderer2?: Object | null,
};
*ReactProviderType类型
export type ReactProviderType<T> = {
$$typeof: Symbol | number,
_context: ReactContext<T>,
};
ReactContext.js代码主要部分为:
xport function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
} else {
if (__DEV__) {
warningWithoutStack(
calculateChangedBits === null ||
typeof calculateChangedBits === 'function',
'createContext: Expected the optional second argument to be a ' +
'function. Instead received: %s',
calculateChangedBits,
);
}
}
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
};
// Consumer和Provider两个属性很有意思,存在循环引用
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
let hasWarnedAboutUsingNestedContextConsumers = false;
let hasWarnedAboutUsingConsumerProvider = false;
if (__DEV__) {
const Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits,
};
Object.defineProperties(Consumer, {
Provider: {
get() {
if (!hasWarnedAboutUsingConsumerProvider) {
hasWarnedAboutUsingConsumerProvider = true;
warning(
false,
'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Provider> instead?',
);
}
return context.Provider;
},
set(_Provider) {
context.Provider = _Provider;
},
},
_currentValue: {
get() {
return context._currentValue;
},
set(_currentValue) {
context._currentValue = _currentValue;
},
},
_currentValue2: {
get() {
return context._currentValue2;
},
set(_currentValue2) {
context._currentValue2 = _currentValue2;
},
},
_threadCount: {
get() {
return context._threadCount;
},
set(_threadCount) {
context._threadCount = _threadCount;
},
},
Consumer: {
get() {
if (!hasWarnedAboutUsingNestedContextConsumers) {
hasWarnedAboutUsingNestedContextConsumers = true;
warning(
false,
'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Consumer> instead?',
);
}
return context.Consumer;
},
},
});
context.Consumer = Consumer;
} else {
context.Consumer = context;
}
if (__DEV__) {
context._currentRenderer = null;
context._currentRenderer2 = null;
}
return context;
}
1.provider和consumer 其实都是context
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
dev环境:
context.Consumer= {Provider: {}, _currentValue: {}, _currentValue2: {}, _threadCount: {},Consumer: {},}
其他环境:
context.Consumer = context;
1.然后你会发现 不管是provider 还是consumer 都是context。
2.
currentValue=defaultValue
3.当使用
<ThemeContext.Provider>
的时候,其实已经拿到整体的context.而使用
<ThemeContext.Consumer>
也拿到了整体的context。
当provider 的值改变的时候,其实consumer 是从当前的context._currentValue 中获取对应的value,静态的方法,自然是不走生命周期的,所以在pureComponent 中状态改变依然能出发按钮的背景颜色的变化。
总结:
* ReactContext本质就是跨域中间的components的通信。
* 1.只要维护好value,没有key 创建的时候给定默认值,通过provider 组件写,通过Consumer 组件来读
* 2.一个provider 可以对应多个consumer,内层provider 能够重写外层provider 值,实际上是读取与之最近的一个provider
* 3.provider 的value pros 发生变化时会通知所有的后代consumer 重新渲染(直接渲染,不走的shouldComponentUpdate)。
* 4.Consumer 没有与之匹配的Provider ,就走default。
* 5.Consumer 也可以不需要Provider 自己跑
* 6.新旧value 变化,走的是 Object.is() 浅比较,因此数据尽量要扁平化
以上是关于react源码分析/createContext的主要内容,如果未能解决你的问题,请参考以下文章
带有 React.createContext 的 Typescript HOC
如何将 React.createContext() 与反应路由器一起使用?
有啥实用的方法可以在组件中调用“React.createContext()”吗?