45
shouldSetTextContent,
46
supportsHydration,
47
supportsSingletons,
48
- isHydratable,
49
- canHydrateInstance,
50
- canHydrateTextInstance,
51
- canHydrateSuspenseInstance,
48
getNextHydratableSibling,
49
getFirstHydratableChild,
50
getFirstHydratableChildWithinContainer,
69
didNotFindHydratableTextInstance,
70
didNotFindHydratableSuspenseInstance,
71
resolveSingletonInstance,
72
+ shouldSkipHydratableForInstance,
73
+ shouldSkipHydratableForTextInstance,
74
+ shouldSkipHydratableForSuspenseInstance,
75
+ canHydrateInstance,
76
+ canHydrateTextInstance,
77
+ canHydrateSuspenseInstance,
78
+ isHydratableType,
79
+ isHydratableText,
80
} from './ReactFiberHostConfig';
81
import {OffscreenLane} from './ReactFiberLane';
82
import {
99
// Hydration errors that were thrown inside this boundary
100
let hydrationErrors: Array<CapturedValue<mixed>> | null = null;
101
102
+let rootOrSingletonContext = false;
103
+
104
function warnIfHydrating() {
105
if (__DEV__) {
106
if (isHydrating) {
136
isHydrating = true;
137
hydrationErrors = null;
138
didSuspendOrErrorDEV = false;
139
+ rootOrSingletonContext = true;
140
return true;
141
}
142
154
isHydrating = true;
155
hydrationErrors = null;
156
didSuspendOrErrorDEV = false;
157
+ rootOrSingletonContext = false;
158
if (treeContext !== null) {
159
restoreSuspendedTreeContext(fiber, treeContext);
160
}
344
warnNonhydratedInstance(returnFiber, fiber);
345
}
346
339
-function tryHydrate(fiber: Fiber, nextInstance: any) {
340
- switch (fiber.tag) {
341
- // HostSingleton is intentionally omitted. the hydration pathway for singletons is non-fallible
342
- // you can find it inlined in claimHydratableSingleton
343
- case HostComponent: {
344
- const type = fiber.type;
345
- const props = fiber.pendingProps;
346
- const instance = canHydrateInstance(nextInstance, type, props);
347
- if (instance !== null) {
348
- fiber.stateNode = (instance: Instance);
349
- hydrationParentFiber = fiber;
350
- nextHydratableInstance = getFirstHydratableChild(instance);
351
- return true;
352
- }
353
- return false;
354
- }
355
- case HostText: {
356
- const text = fiber.pendingProps;
357
- const textInstance = canHydrateTextInstance(nextInstance, text);
358
- if (textInstance !== null) {
359
- fiber.stateNode = (textInstance: TextInstance);
360
- hydrationParentFiber = fiber;
361
- // Text Instances don't have children so there's nothing to hydrate.
362
- nextHydratableInstance = null;
363
- return true;
364
- }
365
- return false;
366
- }
367
- case SuspenseComponent: {
368
- const suspenseInstance: null | SuspenseInstance =
369
- canHydrateSuspenseInstance(nextInstance);
370
- if (suspenseInstance !== null) {
371
- const suspenseState: SuspenseState = {
372
- dehydrated: suspenseInstance,
373
- treeContext: getSuspendedTreeContext(),
374
- retryLane: OffscreenLane,
375
- };
376
- fiber.memoizedState = suspenseState;
377
- // Store the dehydrated fragment as a child fiber.
378
- // This simplifies the code for getHostSibling and deleting nodes,
379
- // since it doesn't have to consider all Suspense boundaries and
380
- // check if they're dehydrated ones or not.
381
- const dehydratedFragment =
382
- createFiberFromDehydratedFragment(suspenseInstance);
383
- dehydratedFragment.return = fiber;
384
- fiber.child = dehydratedFragment;
385
- hydrationParentFiber = fiber;
386
- // While a Suspense Instance does have children, we won't step into
387
- // it during the first pass. Instead, we'll reenter it later.
388
- nextHydratableInstance = null;
389
- return true;
390
- }
391
- return false;
392
- }
393
- default:
394
- return false;
347
+function tryHydrateInstance(fiber: Fiber, nextInstance: any) {
348
+ // fiber is a HostComponent Fiber
349
+ const instance = canHydrateInstance(
350
+ nextInstance,
351
+ fiber.type,
352
+ fiber.pendingProps,
353
+ );
354
+ if (instance !== null) {
355
+ fiber.stateNode = (instance: Instance);
356
+ hydrationParentFiber = fiber;
357
+ nextHydratableInstance = getFirstHydratableChild(instance);
358
+ rootOrSingletonContext = false;
359
+ return true;
360
}
361
+ return false;
362
+}
363
+
364
+function tryHydrateText(fiber: Fiber, nextInstance: any) {
365
+ // fiber is a HostText Fiber
366
+ const text = fiber.pendingProps;
367
+ const textInstance = canHydrateTextInstance(nextInstance, text);
368
+ if (textInstance !== null) {
369
+ fiber.stateNode = (textInstance: TextInstance);
370
+ hydrationParentFiber = fiber;
371
+ // Text Instances don't have children so there's nothing to hydrate.
372
+ nextHydratableInstance = null;
373
+ return true;
374
+ }
375
+ return false;
376
+}
377
+
378
+function tryHydrateSuspense(fiber: Fiber, nextInstance: any) {
379
+ // fiber is a SuspenseComponent Fiber
380
+ const suspenseInstance = canHydrateSuspenseInstance(nextInstance);
381
+ if (suspenseInstance !== null) {
382
+ const suspenseState: SuspenseState = {
383
+ dehydrated: suspenseInstance,
384
+ treeContext: getSuspendedTreeContext(),
385
+ retryLane: OffscreenLane,
386
+ };
387
+ fiber.memoizedState = suspenseState;
388
+ // Store the dehydrated fragment as a child fiber.
389
+ // This simplifies the code for getHostSibling and deleting nodes,
390
+ // since it doesn't have to consider all Suspense boundaries and
391
+ // check if they're dehydrated ones or not.
392
+ const dehydratedFragment =
393
+ createFiberFromDehydratedFragment(suspenseInstance);
394
+ dehydratedFragment.return = fiber;
395
+ fiber.child = dehydratedFragment;
396
+ hydrationParentFiber = fiber;
397
+ // While a Suspense Instance does have children, we won't step into
398
+ // it during the first pass. Instead, we'll reenter it later.
399
+ nextHydratableInstance = null;
400
+ return true;
401
+ }
402
+ return false;
403
}
404
405
function shouldClientRenderOnMismatch(fiber: Fiber) {
431
false,
432
));
433
hydrationParentFiber = fiber;
434
+ rootOrSingletonContext = true;
435
nextHydratableInstance = getFirstHydratableChild(instance);
436
}
437
}
438
439
+function advanceToFirstAttempableInstance(fiber: Fiber) {
440
+ // fiber is HostComponent Fiber
441
+ while (
442
+ nextHydratableInstance &&
443
+ shouldSkipHydratableForInstance(
444
+ nextHydratableInstance,
445
+ fiber.type,
446
+ fiber.pendingProps,
447
+ )
448
+ ) {
449
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
450
+ const instance: HydratableInstance = (nextHydratableInstance: any);
451
+ nextHydratableInstance = getNextHydratableSibling(instance);
452
+ }
453
+}
454
+
455
+function advanceToFirstAttempableTextInstance() {
456
+ while (
457
+ nextHydratableInstance &&
458
+ shouldSkipHydratableForTextInstance(nextHydratableInstance)
459
+ ) {
460
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
461
+ const instance: HydratableInstance = (nextHydratableInstance: any);
462
+ nextHydratableInstance = getNextHydratableSibling(instance);
463
+ }
464
+}
465
+
466
+function advanceToFirstAttempableSuspenseInstance() {
467
+ while (
468
+ nextHydratableInstance &&
469
+ shouldSkipHydratableForSuspenseInstance(nextHydratableInstance)
470
+ ) {
471
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
472
+ const instance: HydratableInstance = (nextHydratableInstance: any);
473
+ nextHydratableInstance = getNextHydratableSibling(instance);
474
+ }
475
+}
476
+
477
function tryToClaimNextHydratableInstance(fiber: Fiber): void {
478
if (!isHydrating) {
479
return;
480
}
435
- if (enableFloat && !isHydratable(fiber.type, fiber.pendingProps)) {
436
- // This fiber never hydrates from the DOM and always does an insert
437
- fiber.flags = (fiber.flags & ~Hydrating) | Placement;
481
+ if (enableFloat) {
482
+ if (!isHydratableType(fiber.type, fiber.pendingProps)) {
483
+ // This fiber never hydrates from the DOM and always does an insert
484
+ fiber.flags = (fiber.flags & ~Hydrating) | Placement;
485
+ isHydrating = false;
486
+ hydrationParentFiber = fiber;
487
+ return;
488
+ }
489
+ }
490
+ const initialInstance = nextHydratableInstance;
491
+ if (rootOrSingletonContext) {
492
+ // We may need to skip past certain nodes in these contexts
493
+ advanceToFirstAttempableInstance(fiber);
494
+ }
495
+ const nextInstance = nextHydratableInstance;
496
+ if (!nextInstance) {
497
+ if (shouldClientRenderOnMismatch(fiber)) {
498
+ warnNonhydratedInstance((hydrationParentFiber: any), fiber);
499
+ throwOnHydrationMismatch(fiber);
500
+ }
501
+ // Nothing to hydrate. Make it an insertion.
502
+ insertNonHydratedInstance((hydrationParentFiber: any), fiber);
503
isHydrating = false;
504
hydrationParentFiber = fiber;
505
+ nextHydratableInstance = initialInstance;
506
return;
507
}
442
- let nextInstance = nextHydratableInstance;
508
+ const firstAttemptedInstance = nextInstance;
509
+ if (!tryHydrateInstance(fiber, nextInstance)) {
510
+ if (shouldClientRenderOnMismatch(fiber)) {
511
+ warnNonhydratedInstance((hydrationParentFiber: any), fiber);
512
+ throwOnHydrationMismatch(fiber);
513
+ }
514
+ // If we can't hydrate this instance let's try the next one.
515
+ // We use this as a heuristic. It's based on intuition and not data so it
516
+ // might be flawed or unnecessary.
517
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
518
+ const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
519
+ if (rootOrSingletonContext) {
520
+ // We may need to skip past certain nodes in these contexts
521
+ advanceToFirstAttempableInstance(fiber);
522
+ }
523
+ if (
524
+ !nextHydratableInstance ||
525
+ !tryHydrateInstance(fiber, nextHydratableInstance)
526
+ ) {
527
+ // Nothing to hydrate. Make it an insertion.
528
+ insertNonHydratedInstance((hydrationParentFiber: any), fiber);
529
+ isHydrating = false;
530
+ hydrationParentFiber = fiber;
531
+ nextHydratableInstance = initialInstance;
532
+ return;
533
+ }
534
+ // We matched the next one, we'll now assume that the first one was
535
+ // superfluous and we'll delete it. Since we can't eagerly delete it
536
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
537
+ // fiber associated with it.
538
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
539
+ }
540
+}
541
+
542
+function tryToClaimNextHydratableTextInstance(fiber: Fiber): void {
543
+ if (!isHydrating) {
544
+ return;
545
+ }
546
+ const text = fiber.pendingProps;
547
+ const isHydratable = isHydratableText(text);
548
+
549
+ const initialInstance = nextHydratableInstance;
550
+ if (rootOrSingletonContext && isHydratable) {
551
+ // We may need to skip past certain nodes in these contexts.
552
+ // We don't skip if the text is not hydratable because we know no hydratables
553
+ // exist which could match this Fiber
554
+ advanceToFirstAttempableTextInstance();
555
+ }
556
+ const nextInstance = nextHydratableInstance;
557
+ if (!nextInstance || !isHydratable) {
558
+ // We exclude non hydrabable text because we know there are no matching hydratables.
559
+ // We either throw or insert depending on the render mode.
560
+ if (shouldClientRenderOnMismatch(fiber)) {
561
+ warnNonhydratedInstance((hydrationParentFiber: any), fiber);
562
+ throwOnHydrationMismatch(fiber);
563
+ }
564
+ // Nothing to hydrate. Make it an insertion.
565
+ insertNonHydratedInstance((hydrationParentFiber: any), fiber);
566
+ isHydrating = false;
567
+ hydrationParentFiber = fiber;
568
+ nextHydratableInstance = initialInstance;
569
+ return;
570
+ }
571
+ const firstAttemptedInstance = nextInstance;
572
+ if (!tryHydrateText(fiber, nextInstance)) {
573
+ if (shouldClientRenderOnMismatch(fiber)) {
574
+ warnNonhydratedInstance((hydrationParentFiber: any), fiber);
575
+ throwOnHydrationMismatch(fiber);
576
+ }
577
+ // If we can't hydrate this instance let's try the next one.
578
+ // We use this as a heuristic. It's based on intuition and not data so it
579
+ // might be flawed or unnecessary.
580
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
581
+ const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
582
+
583
+ if (rootOrSingletonContext && isHydratable) {
584
+ // We may need to skip past certain nodes in these contexts
585
+ advanceToFirstAttempableTextInstance();
586
+ }
587
+
588
+ if (
589
+ !nextHydratableInstance ||
590
+ !tryHydrateText(fiber, nextHydratableInstance)
591
+ ) {
592
+ // Nothing to hydrate. Make it an insertion.
593
+ insertNonHydratedInstance((hydrationParentFiber: any), fiber);
594
+ isHydrating = false;
595
+ hydrationParentFiber = fiber;
596
+ nextHydratableInstance = initialInstance;
597
+ return;
598
+ }
599
+ // We matched the next one, we'll now assume that the first one was
600
+ // superfluous and we'll delete it. Since we can't eagerly delete it
601
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
602
+ // fiber associated with it.
603
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
604
+ }
605
+}
606
+
607
+function tryToClaimNextHydratableSuspenseInstance(fiber: Fiber): void {
608
+ if (!isHydrating) {
609
+ return;
610
+ }
611
+ const initialInstance = nextHydratableInstance;
612
+ if (rootOrSingletonContext) {
613
+ // We may need to skip past certain nodes in these contexts
614
+ advanceToFirstAttempableSuspenseInstance();
615
+ }
616
+ const nextInstance = nextHydratableInstance;
617
if (!nextInstance) {
618
if (shouldClientRenderOnMismatch(fiber)) {
619
warnNonhydratedInstance((hydrationParentFiber: any), fiber);
623
insertNonHydratedInstance((hydrationParentFiber: any), fiber);
624
isHydrating = false;
625
hydrationParentFiber = fiber;
626
+ nextHydratableInstance = initialInstance;
627
return;
628
}
629
const firstAttemptedInstance = nextInstance;
455
- if (!tryHydrate(fiber, nextInstance)) {
630
+ if (!tryHydrateSuspense(fiber, nextInstance)) {
631
if (shouldClientRenderOnMismatch(fiber)) {
632
warnNonhydratedInstance((hydrationParentFiber: any), fiber);
633
throwOnHydrationMismatch(fiber);
635
// If we can't hydrate this instance let's try the next one.
636
// We use this as a heuristic. It's based on intuition and not data so it
637
// might be flawed or unnecessary.
463
- nextInstance = getNextHydratableSibling(firstAttemptedInstance);
638
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
639
const prevHydrationParentFiber: Fiber = (hydrationParentFiber: any);
465
- if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
640
+
641
+ if (rootOrSingletonContext) {
642
+ // We may need to skip past certain nodes in these contexts
643
+ advanceToFirstAttempableSuspenseInstance();
644
+ }
645
+
646
+ if (
647
+ !nextHydratableInstance ||
648
+ !tryHydrateSuspense(fiber, nextHydratableInstance)
649
+ ) {
650
// Nothing to hydrate. Make it an insertion.
651
insertNonHydratedInstance((hydrationParentFiber: any), fiber);
652
isHydrating = false;
653
hydrationParentFiber = fiber;
654
+ nextHydratableInstance = initialInstance;
655
return;
656
}
657
// We matched the next one, we'll now assume that the first one was
801
}
802
803
function popToNextHostParent(fiber: Fiber): void {
619
- let parent = fiber.return;
620
- while (
621
- parent !== null &&
622
- parent.tag !== HostComponent &&
623
- parent.tag !== HostRoot &&
624
- parent.tag !== SuspenseComponent &&
625
- (!(enableHostSingletons && supportsSingletons)
626
- ? true
627
- : parent.tag !== HostSingleton)
628
- ) {
629
- parent = parent.return;
804
+ hydrationParentFiber = fiber.return;
805
+ while (hydrationParentFiber) {
806
+ switch (hydrationParentFiber.tag) {
807
+ case HostRoot:
808
+ case HostSingleton:
809
+ rootOrSingletonContext = true;
810
+ return;
811
+ case HostComponent:
812
+ case SuspenseComponent:
813
+ rootOrSingletonContext = false;
814
+ return;
815
+ default:
816
+ hydrationParentFiber = hydrationParentFiber.return;
817
+ }
818
}
631
- hydrationParentFiber = parent;
819
}
820
821
function popHydrationState(fiber: Fiber): boolean {
942
resetHydrationState,
943
claimHydratableSingleton,
944
tryToClaimNextHydratableInstance,
945
+ tryToClaimNextHydratableTextInstance,
946
+ tryToClaimNextHydratableSuspenseInstance,
947
prepareToHydrateHostInstance,
948
prepareToHydrateHostTextInstance,
949
prepareToHydrateHostSuspenseInstance,