A concept called "Lane" is used inside React.
Initial Lanes implementation by acdlite · Pull Request #18796 · facebook/react
React 18 alpha was recently released, and many of the concurrent features are based on the Lane.
Lane is a 32-bit representation of a task at reconcile time, and the actual code for Lane is as follows;
// https://github.com/facebook/react/blob/9212d994ba939f20a04220a61e9776b488381596/packages/react-reconciler/src/ReactFiberLane.new.js
const NoLane: Lane = 0b0000000000000000000000000000000;
const SyncLane: Lane = 0b0000000000000000000000000000001
const TransitionLanes: Lanes = 0b0000000001111111111111111000000;
const IdleLane: Lanes = 0b0100000000000000000000000000000;
const OffscreenLane: Lane = 0b1000000000000000000000000000000;
As you can see, lanes exist for each type of task, with the exception of NoLane (when there is no task), but basically lanes with higher priority are represented by smaller numbers.
By using 32 bits, bitmask can be used to manipulate lanes. For example, if multiple lanes are bitmasked into one lane, there is no need to compare all lanes relative to each other, which simplifies implementation and saves memory.
Let's take a look at the actual function that performs bitmasking.
export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
return a | b;
}
This function, as the name suggests, merges lanes and returns them. For example, it can be used as follows.
mergeLanes(
NoLane /*0b0000000000000000000000000000000*/,
OffscreenLane /*0b1000000000000000000000000000000*/
)
// => 0b1000000000000000000000000000000
In the above example, a Lane with no tasks (NoLane) is updated to an OffscreenLane. Since Lanes are attached to Fiber, we can update the Lane of the target Fiber as follows.
fiber.lanes = mergeLanes(
fiber.lanes /* NoLane */,
OffscreenLane
)
// => OffscreenLane
Let's take another look at the function isSubsetOfLanes
.
export function isSubsetOfLanes(set: Lanes, subset: Lanes) {
return (set & subset) === subset;
}
This function returns whether or not the result of the AND operation of Lane matches the subset. It is not clear what makes it useful, so I will try to write some more specific patterns.
isSubsetOfLanes(
NonIdleLanes, /*0b0001111111111111111111111111111*/
SyncLane /*0b0000000000000000000000000000001*/
)
// => true. SyncLane is not Idle task
isSubsetOfLanes(
NonIdleLanes, /*0b0001111111111111111111111111111*/
OffscreenLane /*0b1000000000000000000000000000000*/
)
// => false. OffscreenLane is Idle task
isSubsetOfLanes(
TransitionLanes, /*0b0000000001111111111111111000000*/
TransitionLane1 /*0b0000000000000000000000001000000*/
)
// => true. TransitionLane1 is included in TransitionLanes
As mentioned above, isSubsetOfLanes
allows you to determine if the corresponding Lane is a subset of the target Fiber.
For example, there is a function scheduleWorkOnParentPath
. This function, roughly speaking, is responsible for notifying the upper level parent that the lower level child has a task.
// https://github.com/facebook/react/blob/a8964649bb6332cf1f8d723f81ce97cc5a1886ff/packages/react-reconciler/src/ReactFiberNewContext.new.js#L142
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;
}
}
Here, node.return
is the Fiber of a parent or multiple parents, so you can see that it is a function that updates childLanes by following the path of the parent in order. For example, it can be used to tell the parent when the React.Context is updated in children.
In this function, isSubsetOfLanes
is used.
if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
node.childLanes = mergeLanes(node.childLanes, renderLanes);
if (alternate !== null) {
alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
}
} else if (
Since node represents parent here, we can see that we are doing something like this: "If parent.childLanes (node.childLanes
in the code) is not a subset of the target Lane, update parent.childLanes to the value merged with the target Lane." By doing this, we can move the lanes of children to the parent side. As a result, if you look at fiber.childrenLanes at reconcile time, you will know that the lower layer needs to be re-rendered.
In this way, Lanes make it easy to group multiple tasks together and still determine their priority in a few passes; when reconciliation, we can just refer to / update / merge Lanes and focus on the main algorithm. As a result, an architecture that matches the idea of Fiber and Suspense can be realized.
In addition to Lane, there are several other core PR that will be implemented in React 18 alpha, which are detailed in the following thread.
https://twitter.com/rickhanlonii/status/1402771549808214016
https://twitter.com/dan_abramov/status/1402927593406582787
https://twitter.com/acdlite/status/1402982843962343425
Isn't it amazing to think that after all the design, implementation, verification, and design iterations, a major version upgrade has finally been made since Fiber was announced?
Top comments (1)
This is amazing, I wasn't aware of this.
Thanks a lot for information 😍