Skip to content

Commit e50e950

Browse files
committed
[WIP] This will all make sense, soon
1 parent 94518b0 commit e50e950

16 files changed

+1815
-34
lines changed

packages/react-reconciler/src/ReactFiber.js

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
Mode,
3030
ContextProvider,
3131
ContextConsumer,
32+
LoadingComponent,
33+
TimeoutComponent,
3234
} from 'shared/ReactTypeOfWork';
3335
import getComponentName from 'shared/getComponentName';
3436

@@ -42,6 +44,8 @@ import {
4244
REACT_PROVIDER_TYPE,
4345
REACT_CONTEXT_TYPE,
4446
REACT_ASYNC_MODE_TYPE,
47+
REACT_LOADING_TYPE,
48+
REACT_TIMEOUT_TYPE,
4549
} from 'shared/ReactSymbols';
4650

4751
let hasBadMapPolyfill;
@@ -347,6 +351,12 @@ export function createFiberFromElement(
347351
case REACT_RETURN_TYPE:
348352
fiberTag = ReturnComponent;
349353
break;
354+
case REACT_LOADING_TYPE:
355+
fiberTag = LoadingComponent;
356+
break;
357+
case REACT_TIMEOUT_TYPE:
358+
fiberTag = TimeoutComponent;
359+
break;
350360
default: {
351361
if (typeof type === 'object' && type !== null) {
352362
switch (type.$$typeof) {

packages/react-reconciler/src/ReactFiberBeginWork.js

+119-2
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ import {
3030
Mode,
3131
ContextProvider,
3232
ContextConsumer,
33+
LoadingComponent,
34+
TimeoutComponent,
3335
} from 'shared/ReactTypeOfWork';
3436
import {
37+
NoEffect,
3538
PerformedWork,
3639
Placement,
3740
ContentReset,
41+
DidCapture,
3842
Ref,
3943
} from 'shared/ReactTypeOfSideEffect';
4044
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
@@ -83,8 +87,16 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
8387
config: HostConfig<T, P, I, TI, HI, PI, C, CC, CX, PL>,
8488
hostContext: HostContext<C, CX>,
8589
hydrationContext: HydrationContext<C, CX>,
86-
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
87-
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
90+
scheduleWork: (
91+
fiber: Fiber,
92+
startTime: ExpirationTime,
93+
expirationTime: ExpirationTime,
94+
) => void,
95+
computeExpirationForFiber: (
96+
startTime: ExpirationTime,
97+
fiber: Fiber,
98+
) => ExpirationTime,
99+
recalculateCurrentTime: () => ExpirationTime,
88100
) {
89101
const {shouldSetTextContent, shouldDeprioritizeSubtree} = config;
90102

@@ -108,6 +120,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
108120
computeExpirationForFiber,
109121
memoizeProps,
110122
memoizeState,
123+
recalculateCurrentTime,
111124
);
112125

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

720+
function updateLoadingComponent(
721+
current,
722+
workInProgress,
723+
renderExpirationTime,
724+
) {
725+
const nextProps = workInProgress.pendingProps;
726+
const prevProps = workInProgress.memoizedProps;
727+
728+
let nextState = workInProgress.memoizedState;
729+
if (nextState === null) {
730+
nextState = workInProgress.memoizedState = false;
731+
}
732+
const prevState = current === null ? nextState : current.memoizedState;
733+
734+
const updateQueue = workInProgress.updateQueue;
735+
if (updateQueue !== null) {
736+
nextState = workInProgress.memoizedState = processUpdateQueue(
737+
current,
738+
workInProgress,
739+
updateQueue,
740+
null,
741+
nextProps,
742+
renderExpirationTime,
743+
);
744+
}
745+
746+
const isLoading = nextState;
747+
if (hasLegacyContextChanged()) {
748+
// Normally we can bail out on props equality but if context has changed
749+
// we don't do the bailout and we have to reuse existing props instead.
750+
} else if (prevProps === nextProps && prevState === nextState) {
751+
return bailoutOnAlreadyFinishedWork(current, workInProgress);
752+
}
753+
754+
const render = nextProps.children;
755+
const nextChildren = render(isLoading);
756+
workInProgress.memoizedProps = nextProps;
757+
reconcileChildren(current, workInProgress, nextChildren);
758+
return workInProgress.child;
759+
}
760+
761+
function updateTimeoutComponent(
762+
current,
763+
workInProgress,
764+
renderExpirationTime,
765+
) {
766+
const nextProps = workInProgress.pendingProps;
767+
const prevProps = workInProgress.memoizedProps;
768+
769+
let nextState = workInProgress.memoizedState;
770+
if (nextState === null) {
771+
nextState = workInProgress.memoizedState = false;
772+
}
773+
const prevState = current === null ? nextState : current.memoizedState;
774+
775+
const updateQueue = workInProgress.updateQueue;
776+
if (updateQueue !== null) {
777+
nextState = workInProgress.memoizedState = processUpdateQueue(
778+
current,
779+
workInProgress,
780+
updateQueue,
781+
null,
782+
null,
783+
renderExpirationTime,
784+
);
785+
}
786+
787+
if (hasLegacyContextChanged()) {
788+
// Normally we can bail out on props equality but if context has changed
789+
// we don't do the bailout and we have to reuse existing props instead.
790+
} else if (
791+
// Don't bail out if this is a restart
792+
(workInProgress.effectTag & DidCapture) === NoEffect &&
793+
prevProps === nextProps &&
794+
prevState === nextState
795+
) {
796+
return bailoutOnAlreadyFinishedWork(current, workInProgress);
797+
}
798+
799+
if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
800+
nextState = workInProgress.memoizedState = true;
801+
}
802+
803+
const isExpired = nextState;
804+
const render = nextProps.children;
805+
const nextChildren = render(isExpired);
806+
workInProgress.memoizedProps = nextProps;
807+
workInProgress.memoizedState = nextState;
808+
reconcileChildren(current, workInProgress, nextChildren);
809+
return workInProgress.child;
810+
}
811+
707812
function updatePortalComponent(
708813
current,
709814
workInProgress,
@@ -1080,6 +1185,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
10801185
// A return component is just a placeholder, we can just run through the
10811186
// next one immediately.
10821187
return null;
1188+
case LoadingComponent:
1189+
return updateLoadingComponent(
1190+
current,
1191+
workInProgress,
1192+
renderExpirationTime,
1193+
);
1194+
case TimeoutComponent:
1195+
return updateTimeoutComponent(
1196+
current,
1197+
workInProgress,
1198+
renderExpirationTime,
1199+
);
10831200
case HostPortal:
10841201
return updatePortalComponent(
10851202
current,

packages/react-reconciler/src/ReactFiberClassComponent.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,18 @@ function callGetDerivedStateFromCatch(ctor: any, capturedValues: Array<mixed>) {
110110
}
111111

112112
export default function(
113-
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
114-
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
113+
scheduleWork: (
114+
fiber: Fiber,
115+
startTime: ExpirationTime,
116+
expirationTime: ExpirationTime,
117+
) => void,
118+
computeExpirationForFiber: (
119+
startTime: ExpirationTime,
120+
fiber: Fiber,
121+
) => ExpirationTime,
115122
memoizeProps: (workInProgress: Fiber, props: any) => void,
116123
memoizeState: (workInProgress: Fiber, state: any) => void,
124+
recalculateCurrentTime: () => ExpirationTime,
117125
) {
118126
// Class component state updater
119127
const updater = {
@@ -124,7 +132,8 @@ export default function(
124132
if (__DEV__) {
125133
warnOnInvalidCallback(callback, 'setState');
126134
}
127-
const expirationTime = computeExpirationForFiber(fiber);
135+
const currentTime = recalculateCurrentTime();
136+
const expirationTime = computeExpirationForFiber(currentTime, fiber);
128137
const update = {
129138
expirationTime,
130139
partialState,
@@ -135,15 +144,16 @@ export default function(
135144
next: null,
136145
};
137146
insertUpdateIntoFiber(fiber, update);
138-
scheduleWork(fiber, expirationTime);
147+
scheduleWork(fiber, currentTime, expirationTime);
139148
},
140149
enqueueReplaceState(instance, state, callback) {
141150
const fiber = ReactInstanceMap.get(instance);
142151
callback = callback === undefined ? null : callback;
143152
if (__DEV__) {
144153
warnOnInvalidCallback(callback, 'replaceState');
145154
}
146-
const expirationTime = computeExpirationForFiber(fiber);
155+
const currentTime = recalculateCurrentTime();
156+
const expirationTime = computeExpirationForFiber(currentTime, fiber);
147157
const update = {
148158
expirationTime,
149159
partialState: state,
@@ -154,15 +164,16 @@ export default function(
154164
next: null,
155165
};
156166
insertUpdateIntoFiber(fiber, update);
157-
scheduleWork(fiber, expirationTime);
167+
scheduleWork(fiber, currentTime, expirationTime);
158168
},
159169
enqueueForceUpdate(instance, callback) {
160170
const fiber = ReactInstanceMap.get(instance);
161171
callback = callback === undefined ? null : callback;
162172
if (__DEV__) {
163173
warnOnInvalidCallback(callback, 'forceUpdate');
164174
}
165-
const expirationTime = computeExpirationForFiber(fiber);
175+
const currentTime = recalculateCurrentTime();
176+
const expirationTime = computeExpirationForFiber(currentTime, fiber);
166177
const update = {
167178
expirationTime,
168179
partialState: null,
@@ -173,7 +184,7 @@ export default function(
173184
next: null,
174185
};
175186
insertUpdateIntoFiber(fiber, update);
176-
scheduleWork(fiber, expirationTime);
187+
scheduleWork(fiber, currentTime, expirationTime);
177188
},
178189
};
179190

packages/react-reconciler/src/ReactFiberCommitWork.js

+40
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
HostText,
2626
HostPortal,
2727
CallComponent,
28+
LoadingComponent,
29+
TimeoutComponent,
2830
} from 'shared/ReactTypeOfWork';
2931
import ReactErrorUtils from 'shared/ReactErrorUtils';
3032
import {Placement, Update, ContentReset} from 'shared/ReactTypeOfSideEffect';
@@ -33,6 +35,7 @@ import invariant from 'fbjs/lib/invariant';
3335
import {commitCallbacks} from './ReactFiberUpdateQueue';
3436
import {onCommitUnmount} from './ReactFiberDevToolsHook';
3537
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
38+
import {insertUpdateIntoFiber} from './ReactFiberUpdateQueue';
3639
import {logCapturedError} from './ReactFiberErrorLogger';
3740
import getComponentName from 'shared/getComponentName';
3841
import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
@@ -152,6 +155,22 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
152155
}
153156
}
154157

158+
function scheduleExpirationBoundaryRecovery(fiber) {
159+
const currentTime = recalculateCurrentTime();
160+
const expirationTime = computeExpirationForFiber(currentTime, fiber);
161+
const update = {
162+
expirationTime,
163+
partialState: false,
164+
callback: null,
165+
isReplace: true,
166+
isForced: false,
167+
capturedValue: null,
168+
next: null,
169+
};
170+
insertUpdateIntoFiber(fiber, update);
171+
scheduleWork(fiber, currentTime, expirationTime);
172+
}
173+
155174
function commitLifeCycles(
156175
finishedRoot: FiberRoot,
157176
current: Fiber | null,
@@ -226,6 +245,21 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
226245
// We have no life-cycles associated with portals.
227246
return;
228247
}
248+
case LoadingComponent: {
249+
return;
250+
}
251+
case TimeoutComponent: {
252+
const updateQueue = finishedWork.updateQueue;
253+
if (updateQueue !== null) {
254+
const promises = updateQueue.capturedValues;
255+
if (promises !== null) {
256+
Promise.race(promises).then(() =>
257+
scheduleExpirationBoundaryRecovery(finishedWork),
258+
);
259+
}
260+
}
261+
return;
262+
}
229263
default: {
230264
invariant(
231265
false,
@@ -784,6 +818,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
784818
case HostRoot: {
785819
return;
786820
}
821+
case LoadingComponent: {
822+
return;
823+
}
824+
case TimeoutComponent: {
825+
return;
826+
}
787827
default: {
788828
invariant(
789829
false,

packages/react-reconciler/src/ReactFiberCompleteWork.js

+9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
ContextConsumer,
3535
Fragment,
3636
Mode,
37+
LoadingComponent,
38+
TimeoutComponent,
3739
} from 'shared/ReactTypeOfWork';
3840
import {
3941
Placement,
@@ -605,6 +607,13 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
605607
case ReturnComponent:
606608
// Does nothing.
607609
return null;
610+
case LoadingComponent:
611+
return null;
612+
case TimeoutComponent:
613+
if (workInProgress.effectTag & DidCapture) {
614+
workInProgress.effectTag |= Update;
615+
}
616+
return null;
608617
case Fragment:
609618
return null;
610619
case Mode:

0 commit comments

Comments
 (0)