Skip to content

Commit

Permalink
Split fiber.expirationTime into two separate fields
Browse files Browse the repository at this point in the history
Currently, the `expirationTime` field represents the pending work of
both the fiber itself — including new props, state, and context — and of
any updates in that fiber's subtree.

This commit adds a second field called `childExpirationTime`. Now
`expirationTime` only represents the pending work of the fiber itself.
The subtree's pending work is represented by `childExpirationTime`.

The biggest advantage is it requires fewer checks to bailout on already
finished work. For most types of work, if the `expirationTime` does not
match the render expiration time, we can bailout immediately without
any further checks. This won't work for fibers that have
`shouldComponentUpdate` semantics (class components), for which we still
need to check for props and state changes explicitly.
  • Loading branch information
acdlite committed Jul 17, 2018
1 parent 9358aaf commit 6d9bc7e
Show file tree
Hide file tree
Showing 9 changed files with 389 additions and 366 deletions.
5 changes: 4 additions & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Expand Up @@ -662,7 +662,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
'- ' +
// need to explicitly coerce Symbol to a string
(fiber.type ? fiber.type.name || fiber.type.toString() : '[root]'),
'[' + fiber.expirationTime + (fiber.pendingProps ? '*' : '') + ']',
'[' +
fiber.childExpirationTime +
(fiber.pendingProps ? '*' : '') +
']',
);
if (fiber.updateQueue) {
logUpdateQueue(fiber.updateQueue, depth);
Expand Down
17 changes: 15 additions & 2 deletions packages/react-reconciler/src/ReactFiber.js
Expand Up @@ -149,9 +149,12 @@ export type Fiber = {|
lastEffect: Fiber | null,

// Represents a time in the future by which this work should be completed.
// This is also used to quickly determine if a subtree has no pending changes.
// Does not include work found in its subtree.
expirationTime: ExpirationTime,

// This is used to quickly determine if a subtree has no pending changes.
childExpirationTime: ExpirationTime,

// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
Expand Down Expand Up @@ -229,6 +232,7 @@ function FiberNode(
this.lastEffect = null;

this.expirationTime = NoWork;
this.childExpirationTime = NoWork;

this.alternate = null;

Expand Down Expand Up @@ -330,7 +334,15 @@ export function createWorkInProgress(
}
}

workInProgress.expirationTime = expirationTime;
// Don't touching the subtree's expiration time, which has not changed.
workInProgress.childExpirationTime = current.childExpirationTime;
if (pendingProps !== current.pendingProps) {
// This fiber has new props.
workInProgress.expirationTime = expirationTime;
} else {
// This fiber's props have not changed.
workInProgress.expirationTime = current.expirationTime;
}

workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
Expand Down Expand Up @@ -572,6 +584,7 @@ export function assignFiberPropertiesInDEV(
target.firstEffect = source.firstEffect;
target.lastEffect = source.lastEffect;
target.expirationTime = source.expirationTime;
target.childExpirationTime = source.childExpirationTime;
target.alternate = source.alternate;
if (enableProfilerTimer) {
target.actualDuration = source.actualDuration;
Expand Down

0 comments on commit 6d9bc7e

Please sign in to comment.