Context 状态管理
- 在fiber树创建过程中, 如果使用了Context api(具体来说是使用Context.Provider,Context.Consumer,Class.contextType等api), react内部会维护一个栈来保存提供者(Context.Provider)的状态, 供给消费者(Context.Consumer)使用
export type StackCursor<T> = {| current: T |};
// 维护一个全局stack
const valueStack: Array<any> = [];
let index = -1;
// 一个工厂函数, 创建StackCursor对象
function createCursor<T>(defaultValue: T): StackCursor<T> {
return {
current: defaultValue,
};
}
function isEmpty(): boolean {
return index === -1;
}
// 出栈
function pop<T>(cursor: StackCursor<T>, fiber: Fiber): void {
if (index < 0) {
return;
}
cursor.current = valueStack[index];
valueStack[index] = null;
index--;
}
// 入栈
function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {
index++;
valueStack[index] = cursor.current;
cursor.current = value;
}
ReactFiberNewContext.js:
// 定义全局 valueCursor, 用于管理<Context.Provider/>组件的value
const valueCursor: StackCursor<mixed> = createCursor(null);
// 将context当前的值保存到valueCursor中, 并设置context._currentValue为最新值
// 运行完成之后context为最新状态
export function pushProvider<T>(providerFiber: Fiber, nextValue: T): void {
//获取fiber上的context
const context: ReactContext<T> = providerFiber.type._context;
//入栈操作,将context上一次的值入栈(首次valueCursor.current为null,context._currentValue为创建context时的默认值)
push(valueCursor, context._currentValue, providerFiber);
//更新context值为最新值
context._currentValue = nextValue;
}
// 取出valueCursor中保存的旧值, 设置到context._currentValue上.
// 运行完成之后context恢复到上一个状态
export function popProvider(providerFiber: Fiber): void {
const currentValue = valueCursor.current;
//出栈操作
pop(valueCursor, providerFiber);
const context: ReactContext<any> = providerFiber.type._context;
context._currentValue = currentValue;
}
示例
const MyContext = React.createContext(0);
export default function App() {
return (
// 第一级
<MyContext.Provider value={1}>
<MyContext.Consumer>
{value1 => (
//第二级嵌套
<MyContext.Provider value={2}>
<MyContext.Consumer>
{value2 => (
// 第三级嵌套
<MyContext.Provider value={3}>
<MyContext.Consumer>
{value3 => (
<span>
{value1}-{value2}-{value3}
</span>
)}
</MyContext.Consumer>
</MyContext.Provider>
)}
</MyContext.Consumer>
</MyContext.Provider>
)}
</MyContext.Consumer>
</MyContext.Provider>
);
}
在beginWork阶段进行入栈
- reconciler过程中, 每当遇到Context.Provider类型的节点, 则会执行pushProvider
completeWork进行出栈
- beiginWork进行节点遍历,completeWork进行遍历节点的回朔,恰好和栈的出入逻辑上相符
- 每当遇到Context.Provider类型的节点, 则会执行popProvider
创建 Context
- 其初始值保存在context._currentValue
- 创建了context.Provider, context.Consumer2 个ReactElement对象,使得可以通过<MyContext.Provider value={/* 某个值 */}>方式使用
export function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
}
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
// As a workaround to support multiple concurrent renderers, we categorize
// some renderers as primary and others as secondary. We only expect
// there to be two concurrent renderers at most: React Native (primary) and
// Fabric (secondary); React DOM (primary) and React ART (secondary).
// Secondary renderers store their context values on separate fields.
_currentValue: defaultValue,
//_currentValue2为concurrent模式准备的
_currentValue2: defaultValue,
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
context.Consumer = context;
return context;
}
初次构造
- 在fiber树渲染时, 在beginWork中对ContextProvider类型的节点处理
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const updateLanes = workInProgress.lanes;
workInProgress.lanes = NoLanes;
// ...省略无关代码
switch (workInProgress.tag) {
//针对对应的fiber节点进行处理
case ContextProvider:
return updateContextProvider(current, workInProgress, renderLanes);
case ContextConsumer:
return updateContextConsumer(current, workInProgress, renderLanes);
}
}
function updateContextProvider(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
// ...
const providerType: ReactProviderType<any> = workInProgress.type;
const context: ReactContext<any> = providerType._context;
const newProps = workInProgress.pendingProps;
const oldProps = workInProgress.memoizedProps;
// 接收新value
const newValue = newProps.value;
// 更新 ContextProvider._currentValue
pushProvider(workInProgress, newValue);
//说明是对比更新阶段
if (oldProps !== null) {
// ... 更新context的逻辑
}
//进行子节点的比较
const newChildren = newProps.children;
reconcileChildren(current, workInProgress, newChildren, renderLanes);
return workInProgress.child;
}
消费 Context
updateContextConsumer
function updateContextConsumer(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
//context作为fiber的属性存在
let context: ReactContext<any> = workInProgress.type;
const newProps = workInProgress.pendingProps;
const render = newProps.children;
// 读取context
prepareToReadContext(workInProgress, renderLanes);
const newValue = readContext(context, newProps.unstable_observedBits);
let newChildren;
// ...
}
prepareToReadContext、readContext
- Consumer时每遇到一个相同类型的context,构造成链表将当前context挂载到链表上,并返回当前context存储的value
- 因为context的值是通过栈的结构来保存的,consumer拿到的value都是provider预先存入的,所以有多个provider,consumer拿到的是离自己最近的provider
- 因为相同context创造的consumer对应的都是一个fiber节点,所以多个consumer的context都会挂载到同一个fiber的依赖上
// ... 省略无关代码
export function prepareToReadContext(
workInProgress: Fiber,
renderLanes: Lanes,
): void {
// 1. 设置全局变量, 为readContext做准备
currentlyRenderingFiber = workInProgress;
lastContextDependency = null;
lastContextWithAllBitsObserved = null;
//首次消费不进入,在对比更新时才进入
const dependencies = workInProgress.dependencies;
if (dependencies !== null) {
const firstContext = dependencies.firstContext;
if (firstContext !== null) {
//进行更新标识
if (includesSomeLane(dependencies.lanes, renderLanes)) {
// Context list has a pending update. Mark that this fiber performed work.
markWorkInProgressReceivedUpdate();
}
// Reset the work-in-progress list
dependencies.firstContext = null;
}
}
}
// ...
export function readContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
const contextItem = {
context: ((context: any): ReactContext<mixed>),
observedBits: resolvedObservedBits,
next: null,
};
// 1. 构造一个contextItem, 加入到 workInProgress.dependencies链表之后
if (lastContextDependency === null) {
lastContextDependency = contextItem;
//currentlyRenderingFiber即workInProgress
currentlyRenderingFiber.dependencies = {
lanes: NoLanes,
firstContext: contextItem,
responders: null,
};
} else {
//构造链表
lastContextDependency = lastContextDependency.next = contextItem;
}
// 2. 返回 currentValue
return isPrimaryRenderer ? context._currentValue : context._currentValue2;
}
对比更新阶段
进入updateContextProvider
function updateContextProvider(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
const providerType: ReactProviderType<any> = workInProgress.type;
const context: ReactContext<any> = providerType._context;
const newProps = workInProgress.pendingProps;
const oldProps = workInProgress.memoizedProps;
const newValue = newProps.value;
//因为completeWork中会出栈,所以再次遇到Provider能够和初次入栈逻辑相同
pushProvider(workInProgress, newValue);
if (oldProps !== null) {
// 更新阶段进入
const oldValue = oldProps.value;
// 对比 newValue 和 oldValue
// 和Object.is比较效果相同
const changedBits = calculateChangedBits(context, newValue, oldValue);
if (changedBits === 0) {
// value没有变动, 进入 Bailout 逻辑
if (
oldProps.children === newProps.children &&
!hasLegacyContextChanged()
) {
//当前fiber节点无需更新, 循环检测子节点是否需要更新
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderLanes,
);
}
} else {
// value变动, 查找对应的consumers, 并使其能够被更新
propagateContextChange(workInProgress, context, changedBits, renderLanes);
}
}
// ...
}
propagateContextChange
- 当Provider的value变动后,从该fiber开始向下查找依赖的consumer
- 比较依赖的consumer挂载的context链表,如果需要更新,从该fiber开始,往上一直查找父节点并打上更新的标识
export function propagateContextChange(
workInProgress: Fiber,
context: ReactContext<mixed>,
changedBits: number,
renderLanes: Lanes,
): void {
//从当前Provider的第一个子节点开始查找
let fiber = workInProgress.child;
if (fiber !== null) {
// Set the return pointer of the child to the work-in-progress fiber.
fiber.return = workInProgress;
}
//往下遍历fiber,找到所有有依赖Provider的context
while (fiber !== null) {
let nextFiber;
const list = fiber.dependencies;
if (list !== null) {
nextFiber = fiber.child;
let dependency = list.firstContext;
//查找comuser挂载的所有context
while (dependency !== null) {
// 检查dependency中依赖的context是否需要更新
if (
dependency.context === context &&
(dependency.observedBits & changedBits) !== 0
) {
// 符合条件, 安排调度
if (fiber.tag === ClassComponent) {
// class 组件需要创建一个update对象, 添加到updateQueue队列
const update = createUpdate(
NoTimestamp,
pickArbitraryLane(renderLanes),
);
update.tag = ForceUpdate; // 注意ForceUpdate, 保证class组件一定执行render
enqueueUpdate(fiber, update);
}
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
}
// 往上遍历,标记更新
scheduleWorkOnParentPath(fiber.return, renderLanes);
// 标记优先级
list.lanes = mergeLanes(list.lanes, renderLanes);
// 退出查找
break;
}
dependency = dependency.next;
}
}
// ...
fiber = nextFiber;
}
}
scheduleWorkOnParentPath
- 从consumer节点开始, 向上遍历, 修改父路径上所有节点的fiber.childLanes属性, 表明其子节点有改动, 子节点会进入更新逻辑
export function scheduleWorkOnParentPath(
parent: Fiber | null,
renderLanes: Lanes,
) {
// Update the child lanes of all the ancestors, including the alternates.
let node = parent;
while (node !== null) {
const alternate = node.alternate;
if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
node.childLanes = mergeLanes(node.childLanes, renderLanes);
if (alternate !== null) {
alternate.childLanes = mergeLanes(
alternate.childLanes,
renderLanes,
);
}
} else if (
alternate !== null &&
!isSubsetOfLanes(alternate.childLanes, renderLanes)
) {
alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
} else {
// Neither alternate was updated, which means the rest of the
// ancestor path already has sufficient priority.
break;
}
node = node.return;
}
}