15
15
import type { ReactCoroutine , ReactYield } from 'ReactCoroutine' ;
16
16
import type { Fiber } from 'ReactFiber' ;
17
17
import type { PriorityLevel } from 'ReactPriorityLevel' ;
18
+ import type { FiberRoot } from 'ReactFiberRoot' ;
18
19
19
20
var REACT_ELEMENT_TYPE = require ( 'ReactElementSymbol' ) ;
20
21
var {
@@ -26,6 +27,7 @@ var ReactFiber = require('ReactFiber');
26
27
var ReactReifiedYield = require ( 'ReactReifiedYield' ) ;
27
28
var ReactTypeOfSideEffect = require ( 'ReactTypeOfSideEffect' ) ;
28
29
var ReactTypeOfWork = require ( 'ReactTypeOfWork' ) ;
30
+ var ReactFiberRootErrorPhase = require ( 'ReactFiberRootErrorPhase' ) ;
29
31
30
32
var emptyObject = require ( 'emptyObject' ) ;
31
33
var getIteratorFn = require ( 'getIteratorFn' ) ;
@@ -52,6 +54,7 @@ const {
52
54
CoroutineComponent,
53
55
YieldComponent,
54
56
Fragment,
57
+ HostContainer,
55
58
} = ReactTypeOfWork ;
56
59
57
60
const {
@@ -60,6 +63,11 @@ const {
60
63
Deletion,
61
64
} = ReactTypeOfSideEffect ;
62
65
66
+ const {
67
+ NoError,
68
+ HardDeletion,
69
+ } = ReactFiberRootErrorPhase ;
70
+
63
71
function transferRef ( current : ?Fiber , workInProgress : Fiber , element : ReactElement < any > ) {
64
72
if ( typeof element . ref === 'string' ) {
65
73
if ( element . _owner ) {
@@ -480,12 +488,34 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
480
488
return null ;
481
489
}
482
490
491
+ function isFailedErrorBoundary ( fiber : Fiber ) : boolean {
492
+ // Detect if the return fiber is a failed error boundary. If so, we should
493
+ // treat the keys as if they don't match to force a remount. For convenience
494
+ // this check will reset the error boundary flag, so make sure to only call
495
+ // this once per reconciliation.
496
+ switch ( fiber . tag ) {
497
+ case ClassComponent :
498
+ if ( fiber . stateNode . _isFailedErrorBoundary ) {
499
+ delete fiber . stateNode . _isFailedErrorBoundary ;
500
+ return true ;
501
+ }
502
+ return false ;
503
+ case HostContainer :
504
+ const root : FiberRoot = fiber . stateNode ;
505
+ if ( root . errorPhase === HardDeletion ) {
506
+ root . errorPhase = NoError ;
507
+ return true ;
508
+ }
509
+ return false ;
510
+ }
511
+ return false ;
512
+ }
513
+
483
514
function reconcileChildrenArray (
484
515
returnFiber : Fiber ,
485
516
currentFirstChild : ?Fiber ,
486
517
newChildren : Array < * > ,
487
518
priority : PriorityLevel ) : ?Fiber {
488
-
489
519
// This algorithm can't optimize by searching from boths ends since we
490
520
// don't have backpointers on fibers. I'm trying to see how far we can get
491
521
// with that model. If it ends up not being worth the tradeoffs, we can
@@ -502,21 +532,24 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
502
532
// In this first iteration, we'll just live with hitting the bad case
503
533
// (adding everything to a Map) in for every insert/move.
504
534
535
+ const returnFiberIsFailedErrorBoundary = isFailedErrorBoundary ( returnFiber ) ;
536
+
505
537
let resultingFirstChild : ?Fiber = null ;
506
538
let previousNewFiber : ?Fiber = null ;
507
539
508
- let oldFiber = currentFirstChild ;
540
+ // If the parent is an error boundary, setting oldFiber to null ensures
541
+ // that none of the old fibers are reused, the old children are deleted,
542
+ // and new fibers are inserted.
543
+ let oldFiber = returnFiberIsFailedErrorBoundary ? null : currentFirstChild ;
509
544
let lastPlacedIndex = 0 ;
510
545
let newIdx = 0 ;
511
546
let nextOldFiber = null ;
512
547
for ( ; oldFiber && newIdx < newChildren . length ; newIdx ++ ) {
513
- if ( oldFiber ) {
514
- if ( oldFiber . index > newIdx ) {
515
- nextOldFiber = oldFiber ;
516
- oldFiber = null ;
517
- } else {
518
- nextOldFiber = oldFiber . sibling ;
519
- }
548
+ if ( oldFiber . index > newIdx ) {
549
+ nextOldFiber = oldFiber ;
550
+ oldFiber = null ;
551
+ } else {
552
+ nextOldFiber = oldFiber . sibling ;
520
553
}
521
554
const newFiber = updateSlot (
522
555
returnFiber ,
@@ -670,12 +703,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
670
703
element : ReactElement < any > ,
671
704
priority : PriorityLevel
672
705
) : Fiber {
706
+ const returnFiberIsFailedErrorBoundary = isFailedErrorBoundary ( returnFiber ) ;
673
707
const key = element . key ;
674
708
let child = currentFirstChild ;
675
709
while ( child ) {
676
710
// TODO: If key === null and child.key === null, then this only applies to
677
711
// the first item in the list.
678
- if ( child . key === key ) {
712
+ if ( child . key === key && ! returnFiberIsFailedErrorBoundary ) {
679
713
if ( child . type === element . type ) {
680
714
deleteRemainingChildren ( returnFiber , child . sibling ) ;
681
715
const existing = useFiber ( child , priority ) ;
@@ -705,12 +739,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
705
739
coroutine : ReactCoroutine ,
706
740
priority : PriorityLevel
707
741
) : Fiber {
742
+ const returnFiberIsFailedErrorBoundary = isFailedErrorBoundary ( returnFiber ) ;
708
743
const key = coroutine . key ;
709
744
let child = currentFirstChild ;
710
745
while ( child ) {
711
746
// TODO: If key === null and child.key === null, then this only applies to
712
747
// the first item in the list.
713
- if ( child . key === key ) {
748
+ if ( child . key === key && ! returnFiberIsFailedErrorBoundary ) {
714
749
if ( child . tag === CoroutineComponent ) {
715
750
deleteRemainingChildren ( returnFiber , child . sibling ) ;
716
751
const existing = useFiber ( child , priority ) ;
@@ -738,12 +773,13 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
738
773
yieldNode : ReactYield ,
739
774
priority : PriorityLevel
740
775
) : Fiber {
776
+ const returnFiberIsFailedErrorBoundary = isFailedErrorBoundary ( returnFiber ) ;
741
777
const key = yieldNode . key ;
742
778
let child = currentFirstChild ;
743
779
while ( child ) {
744
780
// TODO: If key === null and child.key === null, then this only applies to
745
781
// the first item in the list.
746
- if ( child . key === key ) {
782
+ if ( child . key === key && ! returnFiberIsFailedErrorBoundary ) {
747
783
if ( child . tag === YieldComponent ) {
748
784
deleteRemainingChildren ( returnFiber , child . sibling ) ;
749
785
const existing = useFiber ( child , priority ) ;
0 commit comments