Files
react/packages/react-reconciler/src/ReactFiberHiddenContext.js
Andrew ClarkandGitHub 309c8ad968 Track entangled lanes separately from update lane (#27505)
A small refactor to how the lane entanglement mechanism works. We can
now distinguish between the lane that "spawned" a render task (i.e. a
new update) versus the lanes that it's entangled with. Both the update
lane and the entangled lanes will be included while rendering, but by
keeping them separate, we don't lose the original priority.

In practical terms, this means we can now entangle a low priority update
with a higher priority lane while rendering at the lower priority.

To do this, lanes that are entangled at the root are now tracked using
the same variable that we use to track the "base lanes" when revealing a
previously hidden tree — conceptually, they are the same thing. I also
renamed this variable (from subtreeLanes to entangledRenderLanes) to
better reflect how it's used.

My primary motivation is related to useDeferredValue, which I'll address
in a later PR.
2023-10-15 12:29:52 -04:00

75 lines
2.6 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Fiber} from './ReactInternalTypes';
import type {StackCursor} from './ReactFiberStack';
import type {Lanes} from './ReactFiberLane';
import {createCursor, push, pop} from './ReactFiberStack';
import {
getEntangledRenderLanes,
setEntangledRenderLanes,
} from './ReactFiberWorkLoop';
import {NoLanes, mergeLanes} from './ReactFiberLane';
// TODO: Remove `renderLanes` context in favor of hidden context
type HiddenContext = {
// Represents the lanes that must be included when processing updates in
// order to reveal the hidden content.
// TODO: Remove `subtreeLanes` context from work loop in favor of this one.
baseLanes: number,
...
};
// TODO: This isn't being used yet, but it's intended to replace the
// InvisibleParentContext that is currently managed by SuspenseContext.
export const currentTreeHiddenStackCursor: StackCursor<HiddenContext | null> =
createCursor(null);
export const prevEntangledRenderLanesCursor: StackCursor<Lanes> =
createCursor(NoLanes);
export function pushHiddenContext(fiber: Fiber, context: HiddenContext): void {
const prevEntangledRenderLanes = getEntangledRenderLanes();
push(prevEntangledRenderLanesCursor, prevEntangledRenderLanes, fiber);
push(currentTreeHiddenStackCursor, context, fiber);
// When rendering a subtree that's currently hidden, we must include all
// lanes that would have rendered if the hidden subtree hadn't been deferred.
// That is, in order to reveal content from hidden -> visible, we must commit
// all the updates that we skipped when we originally hid the tree.
setEntangledRenderLanes(
mergeLanes(prevEntangledRenderLanes, context.baseLanes),
);
}
export function reuseHiddenContextOnStack(fiber: Fiber): void {
// This subtree is not currently hidden, so we don't need to add any lanes
// to the render lanes. But we still need to push something to avoid a
// context mismatch. Reuse the existing context on the stack.
push(prevEntangledRenderLanesCursor, getEntangledRenderLanes(), fiber);
push(
currentTreeHiddenStackCursor,
currentTreeHiddenStackCursor.current,
fiber,
);
}
export function popHiddenContext(fiber: Fiber): void {
// Restore the previous render lanes from the stack
setEntangledRenderLanes(prevEntangledRenderLanesCursor.current);
pop(currentTreeHiddenStackCursor, fiber);
pop(prevEntangledRenderLanesCursor, fiber);
}
export function isCurrentTreeHidden(): boolean {
return currentTreeHiddenStackCursor.current !== null;
}