@samitouri / QOS-React / commits / d9d4ad60c5

Updates from inside componentDidMount/Update should have Task priority. This is only observable in incremental mode.

Updates from inside componentDidMount/Update should have Task priority. This is only observable in incremental mode.

Andrew Clark committed Nov 4, 2016 at 13:57 UTC d9d4ad60c540c88efb43a5e53bc42ba93a04ec1c
5 files changed +67 -382
scripts/fiber/tests-failing.txt
+3
@@ -72,6 +72,9 @@ src/renderers/art/__tests__/ReactART-test.js
72 * resolves refs before componentDidMount
73 * resolves refs before componentDidUpdate
74
75 +src/renderers/dom/__tests__/ReactDOMProduction-test.js
76 +* should throw with an error code in production
77 +
78 src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js
79 * should set style attribute when styles exist
80 * should warn when using hyphenated style names
scripts/fiber/tests-passing.txt
+1 -13
@@ -453,7 +453,6 @@ src/renderers/dom/__tests__/ReactDOMProduction-test.js
453 * should use prod React
454 * should handle a simple flow
455 * should call lifecycle methods
456 -* should throw with an error code in production
456
457 src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
458 * should render strings as children
@@ -846,18 +845,6 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
845 * splits deferred work on multiple roots
846 * works on deferred roots in the order they were scheduled
847 * handles interleaved deferred and animation work
849 -* performs animation work in animation callback
850 -* schedules deferred work in animation callback
851 -* schedules deferred work and performs animation work in animation callback
852 -* performs animation work and schedules deferred work in animation callback
853 -* performs deferred work in deferred callback if it has time
854 -* schedules deferred work in deferred callback if it runs out of time
855 -* performs animated work in deferred callback if it has time
856 -* performs animated work and deferred work in deferred callback if it has time
857 -* performs deferred and animated work work in deferred callback if it has time
858 -* schedules animated work in deferred callback if it runs out of time
859 -* schedules animated work and deferred work in deferred callback if it runs out of time
860 -* schedules deferred work and animated work in deferred callback if it runs out of time
848
849 src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
850 * can update child nodes of a host instance
@@ -878,6 +865,7 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
865 * calls setState callback even if component bails out
866 * calls componentWillUnmount after a deletion, even if nested
867 * calls componentDidMount/Update after insertion/update
868 +* schedules sync updates when inside componentDidMount/Update
869 * invokes ref callbacks after insertion/update/unmount
870 * supports string refs
871
src/renderers/shared/fiber/ReactFiberScheduler.js
+3
@@ -209,7 +209,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
209 while (effectfulFiber) {
210 if (effectfulFiber.effectTag & (Update | Callback)) {
211 const current = effectfulFiber.alternate;
212 + const previousPriorityContext = priorityContext;
213 + priorityContext = TaskPriority;
214 commitLifeCycles(current, effectfulFiber);
215 + priorityContext = previousPriorityContext;
216 }
217 const next = effectfulFiber.nextEffect;
218 // Ensure that we clean these up so that we don't accidentally keep them.
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
-369
@@ -449,373 +449,4 @@ describe('ReactIncrementalScheduling', () => {
449 expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
450 expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]);
451 });
452 -
453 - it('performs animation work in animation callback', () => {
454 - class Foo extends React.Component {
455 - componentDidMount() {
456 - // Animation work that will get performed during animation callback
457 - ReactNoop.performAnimationWork(() => {
458 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
459 - });
460 - }
461 - render() {
462 - return <span prop="a:1" />;
463 - }
464 - }
465 -
466 - // Schedule animation work
467 - ReactNoop.performAnimationWork(() => {
468 - ReactNoop.renderToRootWithID(<Foo />, 'a');
469 - });
470 -
471 - // Flushing animation work should flush animation work scheduled inside it
472 - ReactNoop.flushAnimationPri();
473 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
474 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
475 - });
476 -
477 - it('schedules deferred work in animation callback', () => {
478 - class Foo extends React.Component {
479 - componentDidMount() {
480 - // Deferred work that will get scheduled during animation callback
481 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
482 - }
483 - render() {
484 - return <span prop="a:1" />;
485 - }
486 - }
487 -
488 - // Schedule animation work
489 - ReactNoop.performAnimationWork(() => {
490 - ReactNoop.renderToRootWithID(<Foo />, 'a');
491 - });
492 -
493 - // Flushing animation work should not flush the deferred work
494 - ReactNoop.flushAnimationPri();
495 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
496 - expect(ReactNoop.getChildren('b')).toEqual([]);
497 -
498 - // Flush the deferred work
499 - ReactNoop.flushDeferredPri();
500 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
501 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
502 - });
503 -
504 - it('schedules deferred work and performs animation work in animation callback', () => {
505 - let hasScheduled = false;
506 - class Foo extends React.Component {
507 - componentDidMount() {
508 - // Deferred work that will get scheduled during animation callback
509 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
510 - // Animation work that will get performed during animation callback
511 - ReactNoop.performAnimationWork(() => {
512 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
513 - });
514 - // Deferred work that will get scheduled during animation callback
515 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
516 - hasScheduled = true;
517 - }
518 - render() {
519 - return <span prop="a:1" />;
520 - }
521 - }
522 -
523 - // Schedule animation work
524 - ReactNoop.performAnimationWork(() => {
525 - ReactNoop.renderToRootWithID(<Foo />, 'a');
526 - });
527 -
528 - // Flushing animation work should flush animation work scheduled inside it
529 - ReactNoop.flushAnimationPri();
530 - expect(hasScheduled).toBe(true);
531 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
532 - expect(ReactNoop.getChildren('b')).toEqual([]);
533 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
534 - expect(ReactNoop.getChildren('d')).toEqual([]);
535 -
536 - // Flush the deferred work
537 - ReactNoop.flushDeferredPri();
538 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
539 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
540 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
541 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
542 - });
543 -
544 - it('performs animation work and schedules deferred work in animation callback', () => {
545 - let hasScheduled = false;
546 - class Foo extends React.Component {
547 - componentDidMount() {
548 - // Animation work that will get performed during animation callback
549 - ReactNoop.performAnimationWork(() => {
550 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
551 - });
552 - // Deferred work that will get scheduled during animation callback
553 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
554 - // Animation work that will get performed during animation callback
555 - ReactNoop.performAnimationWork(() => {
556 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
557 - });
558 - hasScheduled = true;
559 - }
560 - render() {
561 - return <span prop="a:1" />;
562 - }
563 - }
564 -
565 - // Schedule animation work
566 - ReactNoop.performAnimationWork(() => {
567 - ReactNoop.renderToRootWithID(<Foo />, 'a');
568 - });
569 -
570 - // Flushing animation work should flush animation work scheduled inside it
571 - ReactNoop.flushAnimationPri();
572 - expect(hasScheduled).toBe(true);
573 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
574 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
575 - expect(ReactNoop.getChildren('c')).toEqual([]);
576 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
577 -
578 - // Flush the deferred work
579 - ReactNoop.flushDeferredPri();
580 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
581 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
582 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
583 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
584 - });
585 -
586 - it('performs deferred work in deferred callback if it has time', () => {
587 - class Foo extends React.Component {
588 - componentDidMount() {
589 - // Deferred work that will get performed during deferred callback
590 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
591 - }
592 - render() {
593 - return <span prop="a:1" />;
594 - }
595 - }
596 -
597 - // Schedule deferred work
598 - ReactNoop.renderToRootWithID(<Foo />, 'a');
599 -
600 - // Flushing deferred work should flush deferred work scheduled inside it
601 - ReactNoop.flushDeferredPri();
602 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
603 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
604 - });
605 -
606 - it('schedules deferred work in deferred callback if it runs out of time', () => {
607 - let hasScheduled = false;
608 - class Foo extends React.Component {
609 - componentDidMount() {
610 - // Deferred work that will get scheduled during deferred callback
611 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
612 - hasScheduled = true;
613 - }
614 - render() {
615 - return <span prop="a:1" />;
616 - }
617 - }
618 -
619 - // Schedule deferred work
620 - ReactNoop.renderToRootWithID(<Foo />, 'a');
621 -
622 - // Flush just enough deferred work to schedule more deferred work
623 - ReactNoop.flushDeferredPri(20);
624 - expect(hasScheduled).toBe(true);
625 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
626 - expect(ReactNoop.getChildren('b')).toEqual([]);
627 -
628 - // Flush the rest of the deferred work
629 - ReactNoop.flushDeferredPri(15);
630 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
631 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
632 - });
633 -
634 - it('performs animated work in deferred callback if it has time', () => {
635 - class Foo extends React.Component {
636 - componentDidMount() {
637 - // Animated work that will get performed during deferred callback
638 - ReactNoop.performAnimationWork(() => {
639 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
640 - });
641 - }
642 - render() {
643 - return <span prop="a:1" />;
644 - }
645 - }
646 -
647 - // Schedule deferred work
648 - ReactNoop.renderToRootWithID(<Foo />, 'a');
649 -
650 - // Flushing deferred work should flush animated work scheduled inside it
651 - ReactNoop.flushDeferredPri();
652 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
653 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
654 - });
655 -
656 - it('performs animated work and deferred work in deferred callback if it has time', () => {
657 - class Foo extends React.Component {
658 - componentDidMount() {
659 - // Deferred work that will get performed during deferred callback
660 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
661 - // Animation work that will get performed during deferred callback
662 - ReactNoop.performAnimationWork(() => {
663 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
664 - });
665 - // Deferred work that will get performed during deferred callback
666 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
667 - }
668 - render() {
669 - return <span prop="a:1" />;
670 - }
671 - }
672 -
673 - // Schedule deferred work
674 - ReactNoop.renderToRootWithID(<Foo />, 'a');
675 -
676 - // Flushing deferred work should flush both deferred and animated work scheduled inside it
677 - ReactNoop.flushDeferredPri();
678 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
679 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
680 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
681 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
682 - });
683 -
684 - it('performs deferred and animated work work in deferred callback if it has time', () => {
685 - class Foo extends React.Component {
686 - componentDidMount() {
687 - // Animation work that will get performed during deferred callback
688 - ReactNoop.performAnimationWork(() => {
689 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
690 - });
691 - // Deferred work that will get performed during deferred callback
692 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
693 - // Animation work that will get performed during deferred callback
694 - ReactNoop.performAnimationWork(() => {
695 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
696 - });
697 - }
698 - render() {
699 - return <span prop="a:1" />;
700 - }
701 - }
702 -
703 - // Schedule deferred work
704 - ReactNoop.renderToRootWithID(<Foo />, 'a');
705 -
706 - // Flushing deferred work should flush both deferred and animated work scheduled inside it
707 - ReactNoop.flushDeferredPri();
708 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
709 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
710 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
711 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
712 - });
713 -
714 - it('schedules animated work in deferred callback if it runs out of time', () => {
715 - let hasScheduled = false;
716 - class Foo extends React.Component {
717 - componentDidMount() {
718 - // Animated work that will get scheduled during deferred callback
719 - ReactNoop.performAnimationWork(() => {
720 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
721 - });
722 - hasScheduled = true;
723 - }
724 - render() {
725 - return <span prop="a:1" />;
726 - }
727 - }
728 -
729 - // Schedule deferred work
730 - ReactNoop.renderToRootWithID(<Foo />, 'a');
731 -
732 - // Flush just enough deferred work to schedule animated work
733 - ReactNoop.flushDeferredPri(20);
734 - expect(hasScheduled).toBe(true);
735 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
736 - expect(ReactNoop.getChildren('b')).toEqual([]);
737 -
738 - // Flush the rest of work in an animated callback.
739 - ReactNoop.flushAnimationPri();
740 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
741 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
742 - });
743 -
744 - it('schedules animated work and deferred work in deferred callback if it runs out of time', () => {
745 - let isScheduled = false;
746 - class Foo extends React.Component {
747 - componentDidMount() {
748 - // Deferred work that will get performed during deferred callback
749 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
750 - // Animation work that will get performed during deferred callback
751 - ReactNoop.performAnimationWork(() => {
752 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
753 - });
754 - // Deferred work that will get performed during deferred callback
755 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
756 - isScheduled = true;
757 - }
758 - render() {
759 - return <span prop="a:1" />;
760 - }
761 - }
762 -
763 - // Schedule deferred work
764 - ReactNoop.renderToRootWithID(<Foo />, 'a');
765 -
766 - // Flushing deferred work should schedule both deferred and animated work
767 - ReactNoop.flushDeferredPri(20);
768 - expect(isScheduled).toBe(true);
769 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
770 - expect(ReactNoop.getChildren('b')).toEqual([]);
771 - expect(ReactNoop.getChildren('c')).toEqual([]);
772 - expect(ReactNoop.getChildren('d')).toEqual([]);
773 -
774 - // Flush the rest of the work
775 - ReactNoop.flushDeferredPri();
776 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
777 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
778 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
779 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
780 - });
781 -
782 - it('schedules deferred work and animated work in deferred callback if it runs out of time', () => {
783 - let isScheduled = false;
784 - class Foo extends React.Component {
785 - componentDidMount() {
786 - // Animation work that will get performed during deferred callback
787 - ReactNoop.performAnimationWork(() => {
788 - ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
789 - });
790 - // Deferred work that will get performed during deferred callback
791 - ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
792 - // Animation work that will get performed during deferred callback
793 - ReactNoop.performAnimationWork(() => {
794 - ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
795 - });
796 - isScheduled = true;
797 - }
798 - render() {
799 - return <span prop="a:1" />;
800 - }
801 - }
802 -
803 - // Schedule deferred work
804 - ReactNoop.renderToRootWithID(<Foo />, 'a');
805 -
806 - // Flushing deferred work should schedule both deferred and animated work
807 - ReactNoop.flushDeferredPri(20);
808 - expect(isScheduled).toBe(true);
809 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
810 - expect(ReactNoop.getChildren('b')).toEqual([]);
811 - expect(ReactNoop.getChildren('c')).toEqual([]);
812 - expect(ReactNoop.getChildren('d')).toEqual([]);
813 -
814 - // Flush the rest of the work
815 - ReactNoop.flushDeferredPri();
816 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
817 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
818 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
819 - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
820 - });
452 });
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
+60
@@ -1036,6 +1036,66 @@ describe('ReactIncrementalSideEffects', () => {
1036
1037 });
1038
1039 + it('schedules sync updates when inside componentDidMount/Update', () => {
1040 + var instance;
1041 + var ops = [];
1042 +
1043 + class Foo extends React.Component {
1044 + state = { tick: 0 };
1045 +
1046 + componentDidMount() {
1047 + ops.push('componentDidMount (before setState): ' + this.state.tick);
1048 + this.setState({ tick: 1 });
1049 + // We're in a batch. Update hasn't flushed yet.
1050 + ops.push('componentDidMount (after setState): ' + this.state.tick);
1051 + }
1052 +
1053 + componentDidUpdate() {
1054 + ops.push('componentDidUpdate: ' + this.state.tick);
1055 + if (this.state.tick === 2) {
1056 + ops.push('componentDidUpdate (before setState): ' + this.state.tick);
1057 + this.setState({ tick: 3 });
1058 + ops.push('componentDidUpdate (after setState): ' + this.state.tick);
1059 + // We're in a batch. Update hasn't flushed yet.
1060 + }
1061 + }
1062 +
1063 + render() {
1064 + ops.push('render: ' + this.state.tick);
1065 + instance = this;
1066 + return <span prop={this.state.tick} />;
1067 + }
1068 + }
1069 +
1070 + ReactNoop.render(<Foo />);
1071 +
1072 + ReactNoop.flushDeferredPri(20);
1073 + expect(ops).toEqual([
1074 + 'render: 0',
1075 + 'componentDidMount (before setState): 0',
1076 + 'componentDidMount (after setState): 0',
1077 + // If the setState inside componentDidMount were deferred, there would be
1078 + // no more ops. Because it has Task priority, we get these ops, too:
1079 + 'render: 1',
1080 + 'componentDidUpdate: 1',
1081 + ]);
1082 +
1083 + ops = [];
1084 + instance.setState({ tick: 2 });
1085 + ReactNoop.flushDeferredPri(20);
1086 +
1087 + expect(ops).toEqual([
1088 + 'render: 2',
1089 + 'componentDidUpdate: 2',
1090 + 'componentDidUpdate (before setState): 2',
1091 + 'componentDidUpdate (after setState): 2',
1092 + // If the setState inside componentDidUpdate were deferred, there would be
1093 + // no more ops. Because it has Task priority, we get these ops, too:
1094 + 'render: 3',
1095 + 'componentDidUpdate: 3',
1096 + ]);
1097 + });
1098 +
1099 it('invokes ref callbacks after insertion/update/unmount', () => {
1100
1101 var classInstance = null;