Skip to content

Commit

Permalink
Using expirationTime and ChildExpiration in fiber
Browse files Browse the repository at this point in the history
  • Loading branch information
MQuy committed Jan 31, 2019
1 parent c6a3e58 commit 4e72c81
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 125 deletions.
2 changes: 1 addition & 1 deletion src/ChildFiber.js
Expand Up @@ -3,7 +3,7 @@ import {
createFiberFromElement,
createFiberFromText,
} from "./Fiber";
import { NoEffect, Deletion, Placement } from "./TypeOfSideEffect";
import { Deletion, Placement } from "./TypeOfSideEffect";
import { HostText } from "./TypeOfWork";
import { REACT_ELEMENT_TYPE } from "./createElement";

Expand Down
30 changes: 27 additions & 3 deletions src/Fiber.js
Expand Up @@ -9,6 +9,7 @@ import {
} from "./TypeOfWork";
import { REACT_ASYNC_MODE_TYPE, REACT_STRICT_MODE_TYPE } from "./createElement";
import { AsyncMode, StrictMode } from "./TypeOfMode";
import { NoWork } from "./FiberExpirationTime";

export class FiberNode {
constructor(tag, pendingProps, key, mode) {
Expand All @@ -31,18 +32,28 @@ export class FiberNode {
this.memoizedState = null;

this.mode = mode;

// Effects
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;

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

this.alternate = null;
}
}

export function createWorkInProgress(current, pendingProps, expirationTime) {
let workInProgress = current.alternate;
if (workInProgress == null) {
if (workInProgress === null) {
// We use a double buffering pooling technique because we know that we'll
// only ever need at most two versions of a tree. We pool the "other" unused
// node that we're free to reuse. This is lazily created to avoid allocating
// extra objects for things that are never updated. It also allow us to
// reclaim the extra memory if needed.
workInProgress = new FiberNode(
current.tag,
pendingProps,
Expand All @@ -55,21 +66,34 @@ export function createWorkInProgress(current, pendingProps, expirationTime) {
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;

// We already have an alternate.
// Reset the effect tag.
workInProgress.effectTag = NoEffect;

// The effect list is no longer valid.
workInProgress.nextEffect = null;
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
}

workInProgress.expirationTime = expirationTime;
workInProgress.pendingProps = pendingProps;
// 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;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;

// These will be overridden during the parent's reconciliation
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
Expand Down
157 changes: 110 additions & 47 deletions src/FiberBeginWork.js
Expand Up @@ -15,6 +15,7 @@ import {
Mode,
} from "./TypeOfWork";
import { prepareToUseHooks, finishHooks, bailoutHooks } from "./FiberHooks";
import { NoWork } from "./FiberExpirationTime";

let didReceiveUpdate = false;

Expand All @@ -26,15 +27,23 @@ export function beginWork(current, workInProgress, renderExpirationTime) {
didReceiveUpdate = true;
} else if (updateExpirationTime < renderExpirationTime) {
didReceiveUpdate = false;
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
} else {
didReceiveUpdate = false;
}

// Before entering the begin phase, clear the expiration time.
workInProgress.expirationTime = NoWork;

switch (workInProgress.tag) {
case FunctionalComponent:
const Component = workInProgress.type;
return updateFunctionalComponent(
return updateFunctionComponent(
current,
workInProgress,
Component,
Expand All @@ -49,33 +58,47 @@ export function beginWork(current, workInProgress, renderExpirationTime) {
case HostRoot:
return updateHostRoot(current, workInProgress, renderExpirationTime);
case HostComponent:
return updateHostComponent(current, workInProgress);
return updateHostComponent(current, workInProgress, renderExpirationTime);
case HostText:
return updateHostText(current, workInProgress);
case Mode:
return updateMode(current, workInProgress);
return updateMode(current, workInProgress, renderExpirationTime);
}
}

export function updateHostRoot(current, workInProgress, renderExpirationTime) {
let updateQueue = workInProgress.updateQueue;
if (updateQueue != null) {
let state = processUpdateQueue(
function updateHostRoot(current, workInProgress, renderExpirationTime) {
const updateQueue = workInProgress.updateQueue;
const nextProps = workInProgress.pendingProps;
const prevState = workInProgress.memoizedState;
const prevChildren = prevState !== null ? prevState.element : null;
processUpdateQueue(
current,
workInProgress,
updateQueue,
nextProps,
null,
renderExpirationTime,
);
const nextState = workInProgress.memoizedState;
const nextChildren = nextState.element;

if (nextChildren === prevChildren) {
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
updateQueue,
null,
null,
renderExpirationTime,
);
reconcileChildren(current, workInProgress, state.element);
workInProgress.memoizedState = state;
return workInProgress.child;
}
return bailoutOnAlreadyFinishedWork(current, workInProgress);
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}

export function updateFunctionalComponent(
function updateFunctionComponent(
current,
workInProgress,
Component,
Expand All @@ -98,12 +121,16 @@ export function updateFunctionalComponent(
}

workInProgress.effectTag |= PerformedWork;
reconcileChildren(current, workInProgress, nextChildren);
workInProgress.memoizedProps = nextProps;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}

export function updateHostText(current, workInProgress) {
function updateHostText(current, workInProgress) {
if (current == null) {
workInProgress.effectTag |= Placement;
}
Expand All @@ -112,28 +139,23 @@ export function updateHostText(current, workInProgress) {
return null;
}

export function updateHostComponent(current, workInProgress) {
function updateHostComponent(current, workInProgress, renderExpirationTime) {
if (current == null) {
workInProgress.effectTag |= Placement;
}
const memoizedProps = workInProgress.memoizedProps;
let nextProps = workInProgress.pendingProps || memoizedProps;

if (nextProps == null || memoizedProps === nextProps) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}

let nextChildren = nextProps.children;
reconcileChildren(current, workInProgress, nextChildren);
workInProgress.memoizedProps = nextProps;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}

export function updateClassComponent(
current,
workInProgress,
renderExpirationTime,
) {
function updateClassComponent(current, workInProgress, renderExpirationTime) {
let shouldUpdate;
if (current == null) {
if (!workInProgress.stateNode) {
Expand All @@ -153,7 +175,12 @@ export function updateClassComponent(
renderExpirationTime,
);
}
return finishClassComponent(current, workInProgress, shouldUpdate);
return finishClassComponent(
current,
workInProgress,
shouldUpdate,
renderExpirationTime,
);
}

function updateClassInstance(current, workInProgress, renderExpirationTime) {
Expand Down Expand Up @@ -191,28 +218,63 @@ function updateClassInstance(current, workInProgress, renderExpirationTime) {
return true;
}

function finishClassComponent(current, workInProgress, shouldUpdate) {
function finishClassComponent(
current,
workInProgress,
shouldUpdate,
renderExpirationTime,
) {
if (!shouldUpdate) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}

const instance = workInProgress.stateNode;
const nextChildren = instance.render();

workInProgress.effectTag |= PerformedWork;
reconcileChildren(current, workInProgress, nextChildren);
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
workInProgress.memoizedState = instance.state;
workInProgress.memoizedProps = instance.props;
return workInProgress.child;
}

function bailoutOnAlreadyFinishedWork(current, workInProgress) {
cloneChildFibers(current, workInProgress);
return workInProgress.child;
function bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
) {
// Check if the children have any pending work.
const childExpirationTime = workInProgress.childExpirationTime;
if (
childExpirationTime === NoWork ||
childExpirationTime > renderExpirationTime
) {
// The children don't have any work either. We can skip them.
// TODO: Once we add back resuming, we should check if the children are
// a work-in-progress set. If so, we need to transfer their effects.
return null;
} else {
// This fiber doesn't have work, but its subtree does. Clone the child
// fibers and continue.
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}
}

function reconcileChildren(current, workInProgress, nextChildren) {
const renderExpirationTime = workInProgress.expirationTime;
function reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
) {
if (current == null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
Expand Down Expand Up @@ -240,13 +302,14 @@ function reconcileChildren(current, workInProgress, nextChildren) {
}
}

function updateMode(current, workInProgress) {
function updateMode(current, workInProgress, renderExpirationTime) {
const nextChildren = workInProgress.pendingProps.children;
if (nextChildren == null || workInProgress.memoizedProps === nextChildren) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
reconcileChildren(current, workInProgress, nextChildren);
workInProgress.memoizedProps = nextChildren;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
return workInProgress.child;
}

Expand Down
11 changes: 1 addition & 10 deletions src/FiberCompleteWork.js
Expand Up @@ -13,16 +13,7 @@ import {
import { Never } from "./FiberExpirationTime";

export function completeWork(current, workInProgress, renderExpirationTime) {
let newProps = workInProgress.pendingProps;
if (newProps == null) {
newProps = workInProgress.memoizedProps;
} else if (
workInProgress.expirationTime !== Never ||
renderExpirationTime === Never
) {
// Reset the pending props, unless this was a down-prioritization.
workInProgress.pendingProps = null;
}
const newProps = workInProgress.pendingProps;

switch (workInProgress.tag) {
case FunctionalComponent:
Expand Down

0 comments on commit 4e72c81

Please sign in to comment.