Skip to content

Commit

Permalink
[WIP] This will all make sense, soon
Browse files Browse the repository at this point in the history
  • Loading branch information
acdlite committed Feb 24, 2018
1 parent 94518b0 commit e50e950
Show file tree
Hide file tree
Showing 16 changed files with 1,815 additions and 34 deletions.
10 changes: 10 additions & 0 deletions packages/react-reconciler/src/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
Mode,
ContextProvider,
ContextConsumer,
LoadingComponent,
TimeoutComponent,
} from 'shared/ReactTypeOfWork';
import getComponentName from 'shared/getComponentName';

Expand All @@ -42,6 +44,8 @@ import {
REACT_PROVIDER_TYPE,
REACT_CONTEXT_TYPE,
REACT_ASYNC_MODE_TYPE,
REACT_LOADING_TYPE,
REACT_TIMEOUT_TYPE,
} from 'shared/ReactSymbols';

let hasBadMapPolyfill;
Expand Down Expand Up @@ -347,6 +351,12 @@ export function createFiberFromElement(
case REACT_RETURN_TYPE:
fiberTag = ReturnComponent;
break;
case REACT_LOADING_TYPE:
fiberTag = LoadingComponent;
break;
case REACT_TIMEOUT_TYPE:
fiberTag = TimeoutComponent;
break;
default: {
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
Expand Down
121 changes: 119 additions & 2 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ import {
Mode,
ContextProvider,
ContextConsumer,
LoadingComponent,
TimeoutComponent,
} from 'shared/ReactTypeOfWork';
import {
NoEffect,
PerformedWork,
Placement,
ContentReset,
DidCapture,
Ref,
} from 'shared/ReactTypeOfSideEffect';
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
Expand Down Expand Up @@ -83,8 +87,16 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
config: HostConfig<T, P, I, TI, HI, PI, C, CC, CX, PL>,
hostContext: HostContext<C, CX>,
hydrationContext: HydrationContext<C, CX>,
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
scheduleWork: (
fiber: Fiber,
startTime: ExpirationTime,
expirationTime: ExpirationTime,
) => void,
computeExpirationForFiber: (
startTime: ExpirationTime,
fiber: Fiber,
) => ExpirationTime,
recalculateCurrentTime: () => ExpirationTime,
) {
const {shouldSetTextContent, shouldDeprioritizeSubtree} = config;

Expand All @@ -108,6 +120,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
computeExpirationForFiber,
memoizeProps,
memoizeState,
recalculateCurrentTime,
);

// TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
Expand Down Expand Up @@ -704,6 +717,98 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
return workInProgress.stateNode;
}

function updateLoadingComponent(
current,
workInProgress,
renderExpirationTime,
) {
const nextProps = workInProgress.pendingProps;
const prevProps = workInProgress.memoizedProps;

let nextState = workInProgress.memoizedState;
if (nextState === null) {
nextState = workInProgress.memoizedState = false;
}
const prevState = current === null ? nextState : current.memoizedState;

const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
nextState = workInProgress.memoizedState = processUpdateQueue(
current,
workInProgress,
updateQueue,
null,
nextProps,
renderExpirationTime,
);
}

const isLoading = nextState;
if (hasLegacyContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
} else if (prevProps === nextProps && prevState === nextState) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}

const render = nextProps.children;
const nextChildren = render(isLoading);
workInProgress.memoizedProps = nextProps;
reconcileChildren(current, workInProgress, nextChildren);
return workInProgress.child;
}

function updateTimeoutComponent(
current,
workInProgress,
renderExpirationTime,
) {
const nextProps = workInProgress.pendingProps;
const prevProps = workInProgress.memoizedProps;

let nextState = workInProgress.memoizedState;
if (nextState === null) {
nextState = workInProgress.memoizedState = false;
}
const prevState = current === null ? nextState : current.memoizedState;

const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
nextState = workInProgress.memoizedState = processUpdateQueue(
current,
workInProgress,
updateQueue,
null,
null,
renderExpirationTime,
);
}

if (hasLegacyContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
} else if (
// Don't bail out if this is a restart
(workInProgress.effectTag & DidCapture) === NoEffect &&
prevProps === nextProps &&
prevState === nextState
) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}

if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
nextState = workInProgress.memoizedState = true;
}

const isExpired = nextState;
const render = nextProps.children;
const nextChildren = render(isExpired);
workInProgress.memoizedProps = nextProps;
workInProgress.memoizedState = nextState;
reconcileChildren(current, workInProgress, nextChildren);
return workInProgress.child;
}

function updatePortalComponent(
current,
workInProgress,
Expand Down Expand Up @@ -1080,6 +1185,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
// A return component is just a placeholder, we can just run through the
// next one immediately.
return null;
case LoadingComponent:
return updateLoadingComponent(
current,
workInProgress,
renderExpirationTime,
);
case TimeoutComponent:
return updateTimeoutComponent(
current,
workInProgress,
renderExpirationTime,
);
case HostPortal:
return updatePortalComponent(
current,
Expand Down
27 changes: 19 additions & 8 deletions packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,18 @@ function callGetDerivedStateFromCatch(ctor: any, capturedValues: Array<mixed>) {
}

export default function(
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
scheduleWork: (
fiber: Fiber,
startTime: ExpirationTime,
expirationTime: ExpirationTime,
) => void,
computeExpirationForFiber: (
startTime: ExpirationTime,
fiber: Fiber,
) => ExpirationTime,
memoizeProps: (workInProgress: Fiber, props: any) => void,
memoizeState: (workInProgress: Fiber, state: any) => void,
recalculateCurrentTime: () => ExpirationTime,
) {
// Class component state updater
const updater = {
Expand All @@ -124,7 +132,8 @@ export default function(
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
const expirationTime = computeExpirationForFiber(fiber);
const currentTime = recalculateCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const update = {
expirationTime,
partialState,
Expand All @@ -135,15 +144,16 @@ export default function(
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
scheduleWork(fiber, currentTime, expirationTime);
},
enqueueReplaceState(instance, state, callback) {
const fiber = ReactInstanceMap.get(instance);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'replaceState');
}
const expirationTime = computeExpirationForFiber(fiber);
const currentTime = recalculateCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const update = {
expirationTime,
partialState: state,
Expand All @@ -154,15 +164,16 @@ export default function(
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
scheduleWork(fiber, currentTime, expirationTime);
},
enqueueForceUpdate(instance, callback) {
const fiber = ReactInstanceMap.get(instance);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
const expirationTime = computeExpirationForFiber(fiber);
const currentTime = recalculateCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const update = {
expirationTime,
partialState: null,
Expand All @@ -173,7 +184,7 @@ export default function(
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
scheduleWork(fiber, currentTime, expirationTime);
},
};

Expand Down
40 changes: 40 additions & 0 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
HostText,
HostPortal,
CallComponent,
LoadingComponent,
TimeoutComponent,
} from 'shared/ReactTypeOfWork';
import ReactErrorUtils from 'shared/ReactErrorUtils';
import {Placement, Update, ContentReset} from 'shared/ReactTypeOfSideEffect';
Expand All @@ -33,6 +35,7 @@ import invariant from 'fbjs/lib/invariant';
import {commitCallbacks} from './ReactFiberUpdateQueue';
import {onCommitUnmount} from './ReactFiberDevToolsHook';
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
import {insertUpdateIntoFiber} from './ReactFiberUpdateQueue';
import {logCapturedError} from './ReactFiberErrorLogger';
import getComponentName from 'shared/getComponentName';
import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
Expand Down Expand Up @@ -152,6 +155,22 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
}
}

function scheduleExpirationBoundaryRecovery(fiber) {
const currentTime = recalculateCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const update = {
expirationTime,
partialState: false,
callback: null,
isReplace: true,
isForced: false,
capturedValue: null,
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, currentTime, expirationTime);
}

function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
Expand Down Expand Up @@ -226,6 +245,21 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
// We have no life-cycles associated with portals.
return;
}
case LoadingComponent: {
return;
}
case TimeoutComponent: {
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
const promises = updateQueue.capturedValues;
if (promises !== null) {
Promise.race(promises).then(() =>
scheduleExpirationBoundaryRecovery(finishedWork),
);
}
}
return;
}
default: {
invariant(
false,
Expand Down Expand Up @@ -784,6 +818,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
case HostRoot: {
return;
}
case LoadingComponent: {
return;
}
case TimeoutComponent: {
return;
}
default: {
invariant(
false,
Expand Down
9 changes: 9 additions & 0 deletions packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
ContextConsumer,
Fragment,
Mode,
LoadingComponent,
TimeoutComponent,
} from 'shared/ReactTypeOfWork';
import {
Placement,
Expand Down Expand Up @@ -605,6 +607,13 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
case ReturnComponent:
// Does nothing.
return null;
case LoadingComponent:
return null;
case TimeoutComponent:
if (workInProgress.effectTag & DidCapture) {
workInProgress.effectTag |= Update;
}
return null;
case Fragment:
return null;
case Mode:
Expand Down
Loading

0 comments on commit e50e950

Please sign in to comment.