【React源码解读】Context && ConcurrentMode
context
在线源码地址:https://github.com/facebook/react/blob/master/packages/react/src/ReactContext.js
两种实现方式
- childContextType (17版本将被废弃)
- createContext (16版本更新)
为什么要弃用旧的api?
老的api对context的提供方下层所有组件影响太大了,它会导致它下层所有的组件(即便该组件在没有更新的情况下),它每次更新的情况下,都会重新渲染
demo
import React from 'react'
import PropTypes from 'prop-types'
const { Provider, Consumer } = React.createContext('default')
class Parent extends React.Component {
state = {
childContext: '123',
newContext: '456',
}
getChildContext() {
return { value: this.state.childContext, a: 'aaaaa' }
}
render() {
return (
<>
<div>
<label>childContext:</label>
<input
type="text"
value={this.state.childContext}
onChange={e => this.setState({ childContext: e.target.value })}
/>
</div>
<div>
<label>newContext:</label>
<input
type="text"
value={this.state.newContext}
onChange={e => this.setState({ newContext: e.target.value })}
/>
</div>
<Provider value={this.state.newContext}>{this.props.children}</Provider>
</>
)
}
}
class Parent2 extends React.Component {
// { value: this.state.childContext, a: 'bbbbb' }
getChildContext() {
return { a: 'bbbbb' }
}
render() {
return this.props.children
}
}
function Child1(props, context) {
console.log(context)
return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
}
Child1.contextTypes = {
value: PropTypes.string,
}
class Child2 extends React.Component {
render() {
return (
<p>
childContext: {this.context.value} {this.context.a}
</p>
)
}
}
// Child2.contextType = Consumer
Child2.contextTypes = { // 通过这种方式,告诉react在渲染过程中,Child2组件希望去获取它的父层组件中所有传递的context中的某几个
value: PropTypes.string,
a: PropTypes.string,
}
Parent.childContextTypes = { // 声明传递给子组件的context
value: PropTypes.string,
a: PropTypes.string,
}
Parent2.childContextTypes = {
a: PropTypes.string,
}
export default () => (
<Parent>
<Parent2>
<Child1 />
<Child2 />
</Parent2>
</Parent>
)
源码
export function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
) {
/**
calculateChangedBits: 一个方法,用来计算新老context变化
*/
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
Provider: (null: any),
Consumer: (null: any),
};
/**
$$typeof: 与ReactElement的$$typeof不一样
_currentValue和_currentValue2用处是一样的,只是用的地方不一样,比如不同的平台不一样
_currentValue: 用来记录Prvoider上面提供的value有变化的情况下,就会更新到这个_currentValue上面,就是用来记录最新的context的值的
*/
}
ConcurrentMode
16版本以后提出的功能,其目标让react整体渲染过程有一个优先级排比,并整体的渲染过程能够中断,他就可以进行一个任务的调度,更好的利用cpu性能。react能够让我们去区分一些优先级高低的任务,在进行一个react更新的过程中,优先执行一些较高的任务。
<ConcurrentMode>
<Parent />
</ConcurrentMode>
ConcurrentMode有一个特性,在一个子树当中渲染了ConcurrentMode之后,它下面的所有节点产生的更新 都是一个低优先级的更新
demo
import React, { ConcurrentMode } from 'react'
import { flushSync } from 'react-dom' // 能够强制某一个更新操作的时候,使用一个优先级最高的更新
import './index.css'
class Parent extends React.Component {
state = {
async: true,
num: 1,
length: 2000,
}
componentDidMount() {
this.interval = setInterval(() => {
this.updateNum()
}, 200)
}
componentWillUnmount() {
// 别忘了清除interval
if (this.interval) {
clearInterval(this.interval)
}
}
updateNum() {
const newNum = this.state.num === 3 ? 0 : this.state.num + 1
if (this.state.async) {
this.setState({
num: newNum,
})
} else {
flushSync(() => {
this.setState({
num: newNum,
})
})
}
}
render() {
const children = []
const { length, num, async } = this.state
for (let i = 0; i < length; i++) {
children.push(
<div className="item" key={i}>
{num}
</div>
)
}
return (
<div className="main">
async:{' '}
<input
type="checkbox"
checked={async}
onChange={() => flushSync(() => this.setState({ async: !async }))}
/>
<div className="wrapper">{children}</div>
</div>
)
}
}
export default () => (
<ConcurrentMode>
<Parent />
</ConcurrentMode>
)
源码
// React.js
import {
REACT_CONCURRENT_MODE_TYPE,
REACT_FRAGMENT_TYPE,
REACT_PROFILER_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_SUSPENSE_TYPE,
} from 'shared/ReactSymbols';
...
if (enableStableConcurrentModeAPIs) {
React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.Profiler = REACT_PROFILER_TYPE;
} else {
React.unstable_ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.unstable_Profiler = REACT_PROFILER_TYPE;
}
...
/**
发现ConcurrentMode竟然是一个常量,于是我们去shared/ReactSymbols下一睹
*/
// ReactSymbols.js
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
...
export const REACT_CONCURRENT_MODE_TYPE = hasSymbol
? Symbol.for('react.concurrent_mode')
: 0xeacf;
...
/**
我们发现,ConcurrentMode组件就是一个Symbol,它也没有任何的属性
留有疑问,它到底是如何承载chilren的?后续慢慢深入学习
*/