React Fiber 主机上下文
一、作用
二、 获取当前根主机容器
function getCurrentRootHostContainer(): null | Container {
return rootInstanceStackCursor.current;
}
三、获取根主机容器
function getRootHostContainer(): Container {
const rootInstance = requiredContext(rootInstanceStackCursor.current);
return rootInstance;
}
四、获取主机过渡提供程序
export function getHostTransitionProvider(): Fiber | null {
return hostTransitionProviderCursor.current;
}
五、推送主机容器
备注
push()由 ReactFiberStack#push 实现pop()由 ReactFiberStack#pop 实现
function pushHostContainer(fiber: Fiber, nextRootInstance: Container): void {
// Push current root instance onto the stack;
// This allows us to reset root when portals are popped.
//
// 将当前根实例推入堆栈;
// 这允许我们在弹出门户时重置根。
push(rootInstanceStackCursor, nextRootInstance, fiber);
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
//
// 跟踪上下文以及提供该上下文的 Fiber。
// 这使我们能够仅弹出提供唯一上下文的 Fiber。
push(contextFiberStackCursor, fiber, fiber);
// Finally, we need to push the host context to the stack.
// However, we can't just call getRootHostContext() and push it because
// we'd have a different number of entries on the stack depending on
// whether getRootHostContext() throws somewhere in renderer code or not.
// So we push an empty value first. This lets us safely unwind on errors.
//
// 最后,我们需要将宿主上下文压入栈中。
// 但是,我们不能简单地调用 getRootHostContext() 并将其压入栈中,因为
// 根据 getRootHostContext() 是否在渲染器代码中抛出错误,我们在栈上的条目数量会不同。
// 因此,我们首先压入一个空值。这样可以在出现错误时安全地展开栈。
push(contextStackCursor, null, fiber);
const nextRootContext = getRootHostContext(nextRootInstance);
// Now that we know this function doesn't throw, replace it.
// 既然我们知道这个函数不会抛出异常,就替换它吧。
pop(contextStackCursor, fiber);
push(contextStackCursor, nextRootContext, fiber);
}
六、弹出主机容器
备注
pop()由 ReactFiberStack#pop 实现
function popHostContainer(fiber: Fiber) {
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
pop(rootInstanceStackCursor, fiber);
}
七、获取主机上下文
function getHostContext(): HostContext {
const context = requiredContext(contextStackCursor.current);
return context;
}
八、推送主机上下文
备注
push()由 ReactFiberStack#push 实现pop()由 ReactFiberStack#pop 实现getChildHostContext()由渲染平台实现
function pushHostContext(fiber: Fiber): void {
const stateHook: Hook | null = fiber.memoizedState;
if (stateHook !== null) {
// Only provide context if this fiber has been upgraded by a host
// transition. We use the same optimization for regular host context below.
//
// 仅在此 fiber 已通过宿主转换升级时提供上下文。
// 我们在下面对常规宿主上下文使用相同的优化。
push(hostTransitionProviderCursor, fiber, fiber);
}
const context: HostContext = requiredContext(contextStackCursor.current);
const nextContext = getChildHostContext(context, fiber.type);
// Don't push this Fiber's context unless it's unique.
// 除非这是唯一的,否则不要推动这个 Fiber 的上下文。
if (context !== nextContext) {
// Track the context and the Fiber that provided it.
// This enables us to pop only Fibers that provide unique contexts.
//
// 跟踪上下文以及提供该上下文的 Fiber。
// 这使我们能够仅弹出提供独特上下文的 Fiber。
push(contextFiberStackCursor, fiber, fiber);
push(contextStackCursor, nextContext, fiber);
}
}
九、弹出主机上下文
备注
push()由 ReactFiberStack#push 实现pop()由 ReactFiberStack#pop 实现
function popHostContext(fiber: Fiber): void {
if (contextFiberStackCursor.current === fiber) {
// Do not pop unless this Fiber provided the current context.
// pushHostContext() only pushes Fibers that provide unique contexts.
//
// 除非这个 Fiber 提供了当前上下文,否则不要弹出。
// pushHostContext() 仅推送提供独特上下文的 Fiber。
pop(contextStackCursor, fiber);
pop(contextFiberStackCursor, fiber);
}
if (hostTransitionProviderCursor.current === fiber) {
// Do not pop unless this Fiber provided the current context. This is mostly
// a performance optimization, but conveniently it also prevents a potential
// data race where a host provider is upgraded (i.e. memoizedState becomes
// non-null) during a concurrent event. This is a bit of a flaw in the way
// we upgrade host components, but because we're accounting for it here, it
// should be fine.
//
// 除非这个 Fiber 提供了当前上下文,否则不要弹出。这主要是性能优化,但顺便也防止了潜在的数据
// 竞争,即在并发事件期间宿主提供者被升级(即 memoizedState 变为非空)。这是我们升级宿主组件
// 方式中的一个小缺陷,但因为我们在这里考虑到了,所以应该没问题。
pop(hostTransitionProviderCursor, fiber);
// When popping the transition provider, we reset the context value back
// to `NotPendingTransition`. We can do this because you're not allowed to nest forms. If
// we allowed for multiple nested host transition providers, then we'd
// need to reset this to the parent provider's status.
//
// 当弹出过渡提供者时,我们将上下文值重置回 `NotPendingTransition`。我们之所以可以这样做,是
// 因为不允许嵌套表单。如果我们允许多个嵌套的宿主过渡提供者,那么我们就需要将其重置为父提供者的状态。
if (isPrimaryRenderer) {
HostTransitionContext._currentValue = NotPendingTransition;
} else {
HostTransitionContext._currentValue2 = NotPendingTransition;
}
}
}
十、常量
备注
createCursor()由 ReactFiberStack#createCursor 实现
// 上下文堆栈指针
const contextStackCursor: StackCursor<HostContext | null> = createCursor(null);
// 上下文纤程栈指针
const contextFiberStackCursor: StackCursor<Fiber | null> = createCursor(null);
// 根实例栈指针
const rootInstanceStackCursor: StackCursor<Container | null> =
createCursor(null);
// Represents the nearest host transition provider (in React DOM, a <form />)
// NOTE: Since forms cannot be nested, and this feature is only implemented by
// React DOM, we don't technically need this to be a stack. It could be a single
// module variable instead.
//
// 表示最近的主机过渡提供者(在 React DOM 中,例如 <form />)
// 注意:由于表单不能嵌套,并且此功能仅由 React DOM 实现,
// 我们从技术上不需要将其作为堆栈。它可以是一个单独的模块变量。
// + 主机过渡提供者指针
const hostTransitionProviderCursor: StackCursor<Fiber | null> =
createCursor(null);
十一、工具
1. 所需的上下文
function requiredContext<Value>(c: Value | null): Value {
if (__DEV__) {
if (c === null) {
console.error(
'Expected host context to exist. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
}
}
return c as any;
}