跳到主要内容

ReactFiberLegacyContext ( ReactFiber 传统上下文)

ReactFiberLegacyContext (ReactFiber 传统上下文,因为在每一个方法基本都判断了是否禁用了遗留(旧)上下文。

ReactFiberLegacyContext 是处理 旧版 Context API (在 React 16.3- 之前的 Context )的核心模块,负责在 Fiber 架构下管理旧版 Context 的传递、更新和消费逻辑,让旧版 Context 在链表式的 Fiber 协调流程中正常工作。

为什么保留

尽管 Legacy Context 已废弃,但 React 团队为了 向后兼容 (尤其是像 React Router v4/v5 等库曾依赖它),在 FIber 架构中仍保留逻辑。不过在 Strict Mode 或未来版本中可能逐步移除。

一、 空上下文对象

一个空的被 Object.freeze 冻住的空对象。

export const emptyContextObject: {} = {};
if (__DEV__) {
Object.freeze(emptyContextObject);
}

二、获取未屏蔽的上下文

  • 在禁止使用旧上下文的环境中返回空上下文对象( emptyContextObject
function getUnmaskedContext(
workInProgress: Fiber,
Component: Function,
didPushOwnContextIfProvider: boolean,
): Object {
if (disableLegacyContext) {
return emptyContextObject;
} else {
if (didPushOwnContextIfProvider && isContextProvider(Component)) {
// If the fiber is a context provider itself, when we read its context
// 如果 fiber 本身是一个上下文提供者,当我们读取它的上下文时
// we may have already pushed its own child context on the stack. A context
// 可能已经将它自己的子上下文压入堆栈。一个上下文提供者
// provider should not "see" its own child context. Therefore we read the
// 不应该“看到”它自己的子上下文。因此,对于上下文提供者,我们
// previous (parent) context instead for a context provider.
// 会改为读取之前的(父级)上下文。
return previousContext;
}
return contextStackCursor.current;
}
}

三、缓存上下文

缓存 masked context (即根据 contextTypes 过滤后的 context ),避免重复计算。

function cacheContext(
workInProgress: Fiber,
unmaskedContext: Object,
maskedContext: Object,
): void {
if (disableLegacyContext) {
return;
} else {
const instance = workInProgress.stateNode;
instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
}
}

四、 获取掩码上下文

根据 组件的 contextTypes 从完整 context (unmaskedContext) 中提取所需字段。

  • 同样在屏蔽旧上下文环境返回空上下文 ( emptyContextObject
  • 没有获取到上下文时亦返回空山下文 ( emptyContextObject
function getMaskedContext(
workInProgress: Fiber,
unmaskedContext: Object,
): Object {
if (disableLegacyContext) {
return emptyContextObject;
} else {
const type = workInProgress.type;
const contextTypes = type.contextTypes;
if (!contextTypes) {
return emptyContextObject;
}

// Avoid recreating masked context unless unmasked context has changed.
// 除非未屏蔽的上下文发生变化,否则不要重新创建已屏蔽的上下文。
// Failing to do this will result in unnecessary calls to componentWillReceiveProps.
// 如果不这样做,会导致对 componentWillReceiveProps 的不必要调用。
// This may trigger infinite loops if componentWillReceiveProps ca.00nlls setState.
// 如果 componentWillReceiveProps 调用了 setState,这可能会触发无限循环。
const instance = workInProgress.stateNode;
if (
instance &&
instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
) {
return instance.__reactInternalMemoizedMaskedChildContext;
}

const context: { [string]: $FlowFixMe } = {};
for (const key in contextTypes) {
context[key] = unmaskedContext[key];
}

// Cache unmasked context so we can avoid recreating masked context unless necessary.
// 缓存未屏蔽的上下文,以便我们可以避免不必要地重新创建屏蔽的上下文。
// Context is created before the class component is instantiated so check for instance.
// 上下文是在类组件实例化之前创建的,所以需要检查实例。
if (instance) {
cacheContext(workInProgress, unmaskedContext, context);
}

return context;
}
}

五、上下文更改

比较新旧 context 是否发生变化,用户决定是否需要更新子树

function hasContextChanged(): boolean {
if (disableLegacyContext) {
return false;
} else {
return didPerformWorkStackCursor.current;
}
}

六、判断是否为上下文的提供者

function isContextProvider(type: Function): boolean {
if (disableLegacyContext) {
return false;
} else {
const childContextTypes = type.childContextTypes;
return childContextTypes !== null && childContextTypes !== undefined;
}
}

七、弹出上下文

从 React Fiber 栈弹出上下文

function popContext(fiber: Fiber): void {
if (disableLegacyContext) {
return;
} else {
pop(didPerformWorkStackCursor, fiber);
pop(contextStackCursor, fiber);
}
}

八、弹出顶层上下文对象

从 React Fiber 栈弹出顶层上下文

function popTopLevelContextObject(fiber: Fiber): void {
if (disableLegacyContext) {
return;
} else {
pop(didPerformWorkStackCursor, fiber);
pop(contextStackCursor, fiber);
}
}

九、添加顶层上下文对象

将上下文添加到 React Fiber 栈。用于根节点或 Context 提供者压入 Context 。

function pushTopLevelContextObject(
fiber: Fiber,
context: Object,
didChange: boolean,
): void {
if (disableLegacyContext) {
return;
} else {
if (contextStackCursor.current !== emptyContextObject) {
throw new Error(
'Unexpected context found on stack. ' +
'This error is likely caused by a bug in React. Please file an issue.',
);
}

push(contextStackCursor, context, fiber);
push(didPerformWorkStackCursor, didChange, fiber);
}
}

十、处理子上下文

  • 在禁用旧(遗留)上下文中返回父上下文
function processChildContext(
fiber: Fiber,
type: any,
parentContext: Object,
): Object {
if (disableLegacyContext) {
return parentContext;
} else {
const instance = fiber.stateNode;
const childContextTypes = type.childContextTypes;

// TODO (bvaughn) Replace this behavior with an invariant() in the future.
// 代办 : (bvaughn) 将来的某个时候用 invariant() 替换此行为。
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
// 它目前只在 Fiber 中添加,是为了匹配 Stack 中(无意的)行为。
if (typeof instance.getChildContext !== 'function') {
if (__DEV__) {
const componentName = getComponentNameFromFiber(fiber) || 'Unknown';

if (!warnedAboutMissingGetChildContext[componentName]) {
warnedAboutMissingGetChildContext[componentName] = true;
console.error(
'%s.childContextTypes is specified but there is no getChildContext() method ' +
'on the instance. You can either define getChildContext() on %s or remove ' +
'childContextTypes from it.',
componentName,
componentName,
);
}
}
return parentContext;
}

const childContext = instance.getChildContext();
for (const contextKey in childContext) {
if (!(contextKey in childContextTypes)) {
throw new Error(
`${
getComponentNameFromFiber(fiber) || 'Unknown'
}.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`,
);
}
}

return { ...parentContext, ...childContext };
}
}

十一、添加上下文提供者

function pushContextProvider(workInProgress: Fiber): boolean {
if (disableLegacyContext) {
return false;
} else {
const instance = workInProgress.stateNode;
// We push the context as early as possible to ensure stack integrity.
// 我们尽早推送上下文以确保堆栈完整性。
// If the instance does not exist yet, we will push null at first,
// 如果实例尚不存在,我们将首先推送 null,
// and replace it on the stack later when invalidating the context.
// 然后在使上下文无效时再在堆栈上替换它。
const memoizedMergedChildContext =
(instance && instance.__reactInternalMemoizedMergedChildContext) ||
emptyContextObject;

// Remember the parent context so we can merge with it later.
// 记住父上下文,以便我们稍后可以与之合并。
// Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
// 继承父级的 did-perform-work 值,以避免无意中阻止更新。
previousContext = contextStackCursor.current;
push(contextStackCursor, memoizedMergedChildContext, workInProgress);
push(
didPerformWorkStackCursor,
didPerformWorkStackCursor.current,
workInProgress,
);

return true;
}
}

十二、使上下文提供者失效

function invalidateContextProvider(
workInProgress: Fiber,
type: any,
didChange: boolean,
): void {
if (disableLegacyContext) {
return;
} else {
const instance = workInProgress.stateNode;

if (!instance) {
throw new Error(
'Expected to have an instance by this point. ' +
'This error is likely caused by a bug in React. Please file an issue.',
);
}

if (didChange) {
// Merge parent and own context.
// 合并父组件和自身的上下文。
// Skip this if we're not updating due to sCU.
// 如果不是由于 sCU 导致的更新,则跳过此步骤。
// This avoids unnecessarily recomputing memoized values.
// 这样可以避免不必要地重新计算缓存的值。
const mergedContext = processChildContext(
workInProgress,
type,
previousContext,
);
instance.__reactInternalMemoizedMergedChildContext = mergedContext;

// Replace the old (or empty) context with the new one.
// 用新的上下文替换旧的(或空的)上下文。
// It is important to unwind the context in the reverse order.
// 按相反的顺序取消上下文是很重要的。
pop(didPerformWorkStackCursor, workInProgress);
pop(contextStackCursor, workInProgress);
// Now push the new context and mark that it has changed.
// 现在推送新的上下文并标记它已更改。
push(contextStackCursor, mergedContext, workInProgress);
push(didPerformWorkStackCursor, didChange, workInProgress);
} else {
pop(didPerformWorkStackCursor, workInProgress);
push(didPerformWorkStackCursor, didChange, workInProgress);
}
}
}

十三、 找到当前未屏蔽的上下文

  • 在禁用旧(遗留)上下文中返回空的上下文 ( EmptyContextObject
function findCurrentUnmaskedContext(fiber: Fiber): Object {
if (disableLegacyContext) {
return emptyContextObject;
} else {
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
// makes sense elsewhere
// 目前这只用于 renderSubtreeIntoContainer;不确定在其他地方是否有意义
let node: Fiber = fiber;
do {
switch (node.tag) {
case HostRoot:
return node.stateNode.context;
case ClassComponent: {
const Component = node.type;
if (isContextProvider(Component)) {
return node.stateNode.__reactInternalMemoizedMergedChildContext;
}
break;
}
}
node = node.return;
} while (node !== null);

throw new Error(
'Found unexpected detached subtree parent. ' +
'This error is likely caused by a bug in React. Please file an issue.',
);
}
}

十四、常数

1. 警示

let warnedAboutMissingGetChildContext: { [string]: boolean };

if (__DEV__) {
warnedAboutMissingGetChildContext = {};
}

2. 栈信息

// A cursor to the current merged context object on the stack.
// 堆栈上当前合并上下文对象的指针。
const contextStackCursor: StackCursor<Object> =
createCursor(emptyContextObject);
// A cursor to a boolean indicating whether the context has changed.
// 一个指向布尔值的光标,用于指示上下文是否已更改。
const didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false);
// Keep track of the previous context object that was on the stack.
// 跟踪之前堆栈上的上下文对象。
// We use this to get access to the parent context after we have already
// 我们使用它来在已经推送下一个上下文提供者之后访问父上下文,
// pushed the next context provider, and now need to merge their contexts.
// 并且现在需要合并它们的上下文。
let previousContext: Object = emptyContextObject;