一. useContext
方法介绍
useContext
方法接收一个参数Context
,返回Context
记录的属性值
例如下面这段代码,创建一个CounterContext
,将App
的count
状态值赋值给CounterContext
,子组件通过useContext
方法获取CounterContext
记录的属性值
const CounterContext = createContext()
function HelloWorld() {
const value = useContext(CounterContext)
return <h1>{value}</h1>
}
function App() {
const [count, setCount] = useState(0)
return (
<div>
<h1 onClick={() => setCount(count + 1)}>click</h1>
<CounterContext.Provider value={count}>
<HelloWorld />
</CounterContext.Provider>
</div>
)
}
二. 实现createContext
强烈推荐阅读手写mini React,理解React渲染原理,有助于理解本文章内容
2.1 定义createContext
方法
创建elementType
对象,$$typeof
属性记录ReactElement
对象类型,_currentValue
属性记录传递给子组件的值
export function createContext(defaultValue) {
const context = {
$$typeof: REACT_CONTEXT_TYPE, // Symbol.for('react.context')
_currentValue: defaultValue,
}
context.Provider = context
return context
}
以这段示例代码为例<CounterContext.Provider value={0}><HelloWorld /></CounterContext.Provider>
,创建的ReactElement
对象结构如下
{
$$typeof: Symbol.for('react.transitional.element'),
key: null,
type: {
$$typeof: Symbol.for('react.context'),
_currentValue: null,
Provider: self,
},
props: {
value: 0,
children: {
$$typeof: Symbol.for('react.transitional.element'),
key: null,
type: function HelloWorld() {},
props: {},
},
},
}
2.2 创建ReactElement
对象对应的FiberNode
节点
根据ReactElement
对象的type
属性创建对应的FiberNode
节点,FiberNode
节点的tag
属性值为ContextProvider
function createFiberFromElement(element) {
let fiberTag
const { type } = element
if (typeof type === 'function') {
fiberTag = FunctionComponent
} else if (typeof type === 'string') {
fiberTag = HostComponent
} else {
switch (type.$$typeof) {
// ContextProvider类型FiberNode节点
case REACT_CONTEXT_TYPE:
fiberTag = ContextProvider
break
}
}
const fiber = new FiberNode(fiberTag, element.props)
fiber.key = element.key
fiber.elementType = type
coerceRef(fiber, element)
return fiber
}
2.3 更新ContextProvider._currentValue
在构建虚拟DOM
树阶段,递归遍历到ContextProvider
类型的FiberNode
节点时,会将props
属性的value
赋值给ContextProvider._currentValue
function updateContextProvider(current, workInProgress) {
// 获取ContextProvider
const context = workInProgress.elementType
const newProps = workInProgress.pendingProps
const newValue = newProps.value
// 将传递给子组件的值赋值给ContextProvider._currentValue
context._currentValue = newValue
const newChildren = newProps.children
reconcileChildren(current, workInProgress, newChildren)
return workInProgress.child
}
2.4 定义useContext
方法
返回ContextProvider._currentValue
function useContext(context) {
const value = context._currentValue
return value
}