Skip to content

Commit 061ef03

Browse files
committed
Re-implement begin phase for hydration
1 parent cbe5686 commit 061ef03

File tree

2 files changed

+65
-16
lines changed

2 files changed

+65
-16
lines changed

src/renderers/shared/fiber/ReactFiberBeginWork.js

+61-6
Original file line numberDiff line numberDiff line change
@@ -288,27 +288,37 @@ function reconcile(
288288
nextChildren,
289289
nextProps,
290290
nextState,
291+
false,
291292
renderPriority,
292293
);
293294
return child;
294295
}
295296
exports.reconcile = reconcile;
296297

297-
// Split this out so that it can be shared between the normal reconcile
298-
// function and beginCoroutineComponent, which reconciles against a child
299-
// that is stored on the stateNode.
298+
// Lower level version of reconcile function with more options for special
299+
// cases. My thinking is that, for now, this is preferable to forking, because
300+
// it's really easy to mess up when keeping the forks in sync.
301+
// TODO: Unify this with reconcileChildFibers constructor?
300302
function reconcileImpl(
301303
current: Fiber | null,
302304
workInProgress: Fiber,
303305
child: Fiber | null, // Child to reconcile against
304306
nextChildren: any,
305307
nextProps: any | null,
306308
nextState: mixed | null,
309+
forceMountInPlace: boolean,
307310
renderPriority: PriorityLevel,
308311
): Fiber | null {
309312
// We have new children. Update the child set.
310313
let newChild;
311-
if (current === null) {
314+
if (forceMountInPlace) {
315+
newChild = mountChildFibersInPlace(
316+
workInProgress,
317+
child,
318+
nextChildren,
319+
renderPriority,
320+
);
321+
} else if (current === null) {
312322
if (workInProgress.tag === HostPortal) {
313323
// Portals are special because we don't append the children during mount
314324
// but at commit. Therefore we need to track insertions which the normal
@@ -514,7 +524,7 @@ function resumeOrResetWork(
514524
const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
515525
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
516526
hostContext: HostContext<C, CX>,
517-
hydrationContext: HydrationContext,
527+
hydrationContext: HydrationContext<I, TI>,
518528
scheduleUpdate: (fiber: Fiber, priorityLevel: PriorityLevel) => void,
519529
getPriorityContext: (fiber: Fiber, forceAsync: boolean) => PriorityLevel,
520530
) {
@@ -527,6 +537,12 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
527537
const {pushHostContext, pushHostContainer} = hostContext;
528538
const classUpdater = ClassUpdater(scheduleUpdate, getPriorityContext);
529539

540+
const {
541+
enterHydrationState,
542+
resetHydrationState,
543+
tryToClaimNextHydratableInstance,
544+
} = hydrationContext;
545+
530546
function checkForUpdatedRef(current: Fiber | null, workInProgress: Fiber) {
531547
const ref = workInProgress.ref;
532548
if (ref !== null && (current === null || current.ref !== ref)) {
@@ -578,6 +594,7 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
578594
if (nextState === memoizedState) {
579595
// No new state. The root doesn't have props. Bailout.
580596
// TODO: What about context?
597+
resetHydrationState();
581598
return bailout(
582599
current,
583600
workInProgress,
@@ -589,15 +606,44 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
589606

590607
// The state was updated. We have a new element.
591608
const nextChildren = nextState.element;
609+
610+
// If we don't have any current children this might be the first pass.
611+
// We always try to hydrate. If this isn't a hydration pass there won't
612+
// be any children to hydrate which is effectively the same thing as
613+
// not hydrating.
614+
let forceMountInPlace;
615+
if (
616+
(current === null || current.child === null) &&
617+
enterHydrationState(workInProgress)
618+
) {
619+
// Ensure that children mount into this root without tracking
620+
// side-effects. This ensures that we don't store Placement effects on
621+
// nodes that will be hydrated.
622+
forceMountInPlace = true;
623+
624+
// This is a bit of a hack. We track the host root as a placement to
625+
// know that we're currently in a mounting state. That way isMounted
626+
// works as expected. We must reset this before committing.
627+
// TODO: Delete this when we delete isMounted and findDOMNode.
628+
workInProgress.effectTag |= Placement;
629+
} else {
630+
// Otherwise, reset the hydration state
631+
resetHydrationState();
632+
forceMountInPlace = false;
633+
}
634+
592635
// Reconcile the children.
593-
return reconcile(
636+
const child = workInProgress.child = reconcileImpl(
594637
current,
595638
workInProgress,
639+
workInProgress.child,
596640
nextChildren,
597641
null,
598642
nextState,
643+
forceMountInPlace,
599644
renderPriority,
600645
);
646+
return child;
601647
}
602648

603649
function beginHostPortal(
@@ -632,6 +678,10 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
632678
): Fiber | null {
633679
pushHostContext(workInProgress);
634680

681+
if (current === null) {
682+
tryToClaimNextHydratableInstance(workInProgress);
683+
}
684+
635685
const memoizedProps = workInProgress.memoizedProps;
636686

637687
// Check if the ref has changed and schedule an effect. This should happen
@@ -698,6 +748,10 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
698748
nextProps: any,
699749
renderPriority: PriorityLevel,
700750
): Fiber | null {
751+
if (current === null) {
752+
tryToClaimNextHydratableInstance(workInProgress);
753+
}
754+
701755
const memoizedProps = workInProgress.memoizedProps;
702756
if (nextProps === memoizedProps) {
703757
return bailout(current, workInProgress, nextProps, null, renderPriority);
@@ -1297,6 +1351,7 @@ const BeginWork = function<T, P, I, TI, PI, C, CX, PL>(
12971351
nextCoroutine.children,
12981352
nextCoroutine,
12991353
null,
1354+
false,
13001355
renderPriority,
13011356
);
13021357
return child;

src/renderers/shared/fiber/ReactFiberHydrationContext.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,15 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
9090
childToDelete.stateNode = instance;
9191
childToDelete.return = returnFiber;
9292
// Deletions are added in reversed order so we add it to the front.
93-
const last = returnFiber.progressedLastDeletion;
93+
const last = returnFiber.lastDeletion;
9494
if (last !== null) {
9595
last.nextEffect = childToDelete;
96-
returnFiber.progressedLastDeletion = childToDelete;
96+
returnFiber.lastDeletion = childToDelete;
9797
} else {
98-
returnFiber.progressedFirstDeletion = returnFiber.progressedLastDeletion = childToDelete;
98+
returnFiber.firstDeletion = returnFiber.lastDeletion = childToDelete;
9999
}
100+
childToDelete.nextEffect = null;
100101
childToDelete.effectTag = Deletion;
101-
102-
if (returnFiber.lastEffect !== null) {
103-
returnFiber.lastEffect.nextEffect = childToDelete;
104-
returnFiber.lastEffect = childToDelete;
105-
} else {
106-
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
107-
}
108102
}
109103

110104
function tryToClaimNextHydratableInstance(fiber: Fiber) {

0 commit comments

Comments
 (0)