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的?后續慢慢深入學習
*/