@@ -795,6 +795,172 @@ describe('ReactIncrementalSideEffects', () => {
795795 // moves to "current" without flushing due to having lower priority. Does this
796796 // even happen? Maybe a child doesn't get processed because it is lower prio?
797797
798+ it ( 'does not drop priority from a progressed subtree' , ( ) => {
799+ let ops = [ ] ;
800+ let lowPri ;
801+ let highPri ;
802+
803+ function LowPriDidComplete ( ) {
804+ ops . push ( 'LowPriDidComplete' ) ;
805+ // Because this is terminal, beginning work on LowPriDidComplete implies
806+ // that LowPri will be completed before the scheduler yields.
807+ return null ;
808+ }
809+
810+ class LowPri extends React . Component {
811+ state = { step : 0 } ;
812+ render ( ) {
813+ ops . push ( 'LowPri' ) ;
814+ lowPri = this ;
815+ return [
816+ < span key = "1" prop = { this . state . step } /> ,
817+ < LowPriDidComplete key = "2" /> ,
818+ ] ;
819+ }
820+ }
821+
822+ function LowPriSibling ( ) {
823+ ops . push ( 'LowPriSibling' ) ;
824+ return null ;
825+ }
826+
827+ class HighPri extends React . Component {
828+ state = { step : 0 } ;
829+ render ( ) {
830+ ops . push ( 'HighPri' ) ;
831+ highPri = this ;
832+ return < span prop = { this . state . step } /> ;
833+ }
834+ }
835+
836+ function App ( ) {
837+ ops . push ( 'App' ) ;
838+ return [
839+ < div key = "1" >
840+ < LowPri />
841+ < LowPriSibling />
842+ </ div > ,
843+ < div key = "2" > < HighPri /> </ div > ,
844+ ] ;
845+ }
846+
847+ ReactNoop . render ( < App /> ) ;
848+ ReactNoop . flush ( ) ;
849+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ div ( span ( 0 ) ) , div ( span ( 0 ) ) ] ) ;
850+ ops = [ ] ;
851+
852+ lowPri . setState ( { step : 1 } ) ;
853+ // Do just enough work to begin LowPri
854+ ReactNoop . flushDeferredPri ( 30 ) ;
855+ expect ( ops ) . toEqual ( [ 'LowPri' ] ) ;
856+ // Now we'll do one more tick of work to complete LowPri. Because LowPri
857+ // has a sibling, the parent div of LowPri has not yet completed.
858+ ReactNoop . flushDeferredPri ( 10 ) ;
859+ expect ( ops ) . toEqual ( [ 'LowPri' , 'LowPriDidComplete' ] ) ;
860+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
861+ div ( span ( 0 ) ) , // Complete, but not yet updated
862+ div ( span ( 0 ) ) ,
863+ ] ) ;
864+ ops = [ ] ;
865+
866+ // Interrupt with high pri update
867+ ReactNoop . performAnimationWork ( ( ) => highPri . setState ( { step : 1 } ) ) ;
868+ ReactNoop . flushAnimationPri ( ) ;
869+ expect ( ops ) . toEqual ( [ 'HighPri' ] ) ;
870+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
871+ div ( span ( 0 ) ) , // Completed, but not yet updated
872+ div ( span ( 1 ) ) ,
873+ ] ) ;
874+ ops = [ ] ;
875+
876+ ReactNoop . flush ( ) ;
877+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ div ( span ( 1 ) ) , div ( span ( 1 ) ) ] ) ;
878+ } ) ;
879+
880+ it ( 'does not complete already completed work' , ( ) => {
881+ let ops = [ ] ;
882+ let lowPri ;
883+ let highPri ;
884+
885+ function LowPriDidComplete ( ) {
886+ ops . push ( 'LowPriDidComplete' ) ;
887+ // Because this is terminal, beginning work on LowPriDidComplete implies
888+ // that LowPri will be completed before the scheduler yields.
889+ return null ;
890+ }
891+
892+ class LowPri extends React . Component {
893+ state = { step : 0 } ;
894+ render ( ) {
895+ ops . push ( 'LowPri' ) ;
896+ lowPri = this ;
897+ return [
898+ < span key = "1" prop = { this . state . step } /> ,
899+ < LowPriDidComplete key = "2" /> ,
900+ ] ;
901+ }
902+ }
903+
904+ function LowPriSibling ( ) {
905+ ops . push ( 'LowPriSibling' ) ;
906+ return null ;
907+ }
908+
909+ class HighPri extends React . Component {
910+ state = { step : 0 } ;
911+ render ( ) {
912+ ops . push ( 'HighPri' ) ;
913+ highPri = this ;
914+ return < span prop = { this . state . step } /> ;
915+ }
916+ }
917+
918+ function App ( ) {
919+ ops . push ( 'App' ) ;
920+ return [
921+ < div key = "1" >
922+ < LowPri />
923+ < LowPriSibling />
924+ </ div > ,
925+ < div key = "2" > < HighPri /> </ div > ,
926+ ] ;
927+ }
928+
929+ ReactNoop . render ( < App /> ) ;
930+ ReactNoop . flush ( ) ;
931+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ div ( span ( 0 ) ) , div ( span ( 0 ) ) ] ) ;
932+ ops = [ ] ;
933+
934+ lowPri . setState ( { step : 1 } ) ;
935+ // Do just enough work to begin LowPri
936+ ReactNoop . flushDeferredPri ( 30 ) ;
937+ expect ( ops ) . toEqual ( [ 'LowPri' ] ) ;
938+ // Now we'll do one more tick of work to complete LowPri. Because LowPri
939+ // has a sibling, the parent div of LowPri has not yet completed.
940+ ReactNoop . flushDeferredPri ( 10 ) ;
941+ expect ( ops ) . toEqual ( [ 'LowPri' , 'LowPriDidComplete' ] ) ;
942+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
943+ div ( span ( 0 ) ) , // Complete, but not yet updated
944+ div ( span ( 0 ) ) ,
945+ ] ) ;
946+ ops = [ ] ;
947+
948+ // Interrupt with high pri update
949+ ReactNoop . performAnimationWork ( ( ) => highPri . setState ( { step : 1 } ) ) ;
950+ ReactNoop . flushAnimationPri ( ) ;
951+ expect ( ops ) . toEqual ( [ 'HighPri' ] ) ;
952+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
953+ div ( span ( 0 ) ) , // Completed, but not yet updated
954+ div ( span ( 1 ) ) ,
955+ ] ) ;
956+ ops = [ ] ;
957+
958+ // If this is not enough to commit the rest of the work, that means we're
959+ // not bailing out on the already-completed LowPri tree.
960+ ReactNoop . flushDeferredPri ( 45 ) ;
961+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ div ( span ( 1 ) ) , div ( span ( 1 ) ) ] ) ;
962+ } ) ;
963+
798964 it ( 'calls callback after update is flushed' , ( ) => {
799965 let instance ;
800966 class Foo extends React . Component {
0 commit comments