Confirm that a shallow bailout does not drop work in the child
Includes a test that confirms that work that is bailed out before completing can be reused without dropping the entire subtree.
Andrew Clark committed
Jan 7, 2017 at 17:26 UTC
d17b9a13f8519c349c80c3ef2e95361e15f6bdf0
3 files changed
+80
-1
scripts/fiber/tests-passing.txt
+1
@@ -1143,6 +1143,7 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
1143
* can resume work in a subtree even when a parent bails out
1144
* can resume work in a bailed subtree within one pass
1145
* can reuse work done after being preempted
1146
+* can reuse work that began but did not complete, after being preempted
1147
* can reuse work if shouldComponentUpdate is false, after being preempted
1148
* memoizes work even if shouldComponentUpdate returns false
1149
* can update in the middle of a tree using setState
src/renderers/shared/fiber/ReactFiberCompleteWork.js
-1
@@ -266,7 +266,6 @@ module.exports = function<T, P, I, TI, PI, C, CX>(
266
const textInstance = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress);
267
workInProgress.stateNode = textInstance;
268
}
269
- workInProgress.memoizedProps = newText;
269
return null;
270
case CoroutineComponent:
271
return moveCoroutineToHandlerPhase(current, workInProgress);
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+79
@@ -541,6 +541,85 @@ describe('ReactIncremental', () => {
541
542
});
543
544
+ it('can reuse work that began but did not complete, after being preempted', () => {
545
+ let ops = [];
546
+ let child;
547
+ let sibling;
548
+
549
+ function GreatGrandchild() {
550
+ ops.push('GreatGrandchild');
551
+ return <div />;
552
+ }
553
+
554
+ function Grandchild() {
555
+ ops.push('Grandchild');
556
+ return <GreatGrandchild />;
557
+ }
558
+
559
+ class Child extends React.Component {
560
+ state = { step: 0 };
561
+ render() {
562
+ child = this;
563
+ ops.push('Child');
564
+ return <Grandchild />;
565
+ }
566
+ }
567
+
568
+ class Sibling extends React.Component {
569
+ render() {
570
+ ops.push('Sibling');
571
+ sibling = this;
572
+ return <div />;
573
+ }
574
+ }
575
+
576
+ function Parent() {
577
+ ops.push('Parent');
578
+ return [
579
+ // The extra div is necessary because when Parent bails out during the
580
+ // high priority update, its progressedPriority is set to high.
581
+ // So its direct children cannot be reused when we resume at
582
+ // low priority. I think this would be fixed by changing
583
+ // pendingWorkPriority and progressedPriority to be the priority of
584
+ // the children only, not including the fiber itself.
585
+ <div><Child /></div>,
586
+ <Sibling />,
587
+ ];
588
+ }
589
+
590
+ ReactNoop.render(<Parent />);
591
+ ReactNoop.flush();
592
+ ops = [];
593
+
594
+ // Begin working on a low priority update to Child, but stop before
595
+ // GreatGrandchild. Child and Grandchild begin but don't complete.
596
+ child.setState({ step: 1 });
597
+ ReactNoop.flushDeferredPri(30);
598
+ expect(ops).toEqual([
599
+ 'Child',
600
+ 'Grandchild',
601
+ ]);
602
+
603
+ // Interrupt the current low pri work with a high pri update elsewhere in
604
+ // the tree.
605
+ ops = [];
606
+ ReactNoop.performAnimationWork(() => {
607
+ sibling.setState({});
608
+ });
609
+ ReactNoop.flushAnimationPri();
610
+ expect(ops).toEqual(['Sibling']);
611
+
612
+ // Continue the low pri work. The work on Child and GrandChild was memoized
613
+ // so they should not be worked on again.
614
+ ops = [];
615
+ ReactNoop.flush();
616
+ expect(ops).toEqual([
617
+ // No Child
618
+ // No Grandchild
619
+ 'GreatGrandchild',
620
+ ]);
621
+ });
622
+
623
it('can reuse work if shouldComponentUpdate is false, after being preempted', () => {
624
625
var ops = [];