@@ -1144,6 +1144,90 @@ function loadModules({
11441144 ] ) ;
11451145 } ) ;
11461146 } ) ;
1147+
1148+ it ( 'does not warn about state updates for unmounted components with pending passive unmounts' , ( ) => {
1149+ let completePendingRequest = null ;
1150+ function Component ( ) {
1151+ Scheduler . unstable_yieldValue ( 'Component' ) ;
1152+ const [ didLoad , setDidLoad ] = React . useState ( false ) ;
1153+ React . useLayoutEffect ( ( ) => {
1154+ Scheduler . unstable_yieldValue ( 'layout create' ) ;
1155+ return ( ) => {
1156+ Scheduler . unstable_yieldValue ( 'layout destroy' ) ;
1157+ } ;
1158+ } , [ ] ) ;
1159+ React . useEffect ( ( ) => {
1160+ Scheduler . unstable_yieldValue ( 'passive create' ) ;
1161+ // Mimic an XHR request with a complete handler that updates state.
1162+ completePendingRequest = ( ) => setDidLoad ( true ) ;
1163+ return ( ) => {
1164+ Scheduler . unstable_yieldValue ( 'passive destroy' ) ;
1165+ } ;
1166+ } , [ ] ) ;
1167+ return didLoad ;
1168+ }
1169+
1170+ act ( ( ) => {
1171+ ReactNoop . renderToRootWithID ( < Component /> , 'root' , ( ) =>
1172+ Scheduler . unstable_yieldValue ( 'Sync effect' ) ,
1173+ ) ;
1174+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1175+ 'Component' ,
1176+ 'layout create' ,
1177+ 'Sync effect' ,
1178+ ] ) ;
1179+ ReactNoop . flushPassiveEffects ( ) ;
1180+ expect ( Scheduler ) . toHaveYielded ( [ 'passive create' ] ) ;
1181+
1182+ // Unmount but don't process pending passive destroy function
1183+ ReactNoop . unmountRootWithID ( 'root' ) ;
1184+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'layout destroy' ] ) ;
1185+
1186+ // Simulate an XHR completing, which will cause a state update-
1187+ // but should not log a warning.
1188+ completePendingRequest ( ) ;
1189+
1190+ ReactNoop . flushPassiveEffects ( ) ;
1191+ expect ( Scheduler ) . toHaveYielded ( [ 'passive destroy' ] ) ;
1192+ } ) ;
1193+ } ) ;
1194+
1195+ it ( 'still warns about state updates for unmounted components with no pending passive unmounts' , ( ) => {
1196+ let completePendingRequest = null ;
1197+ function Component ( ) {
1198+ Scheduler . unstable_yieldValue ( 'Component' ) ;
1199+ const [ didLoad , setDidLoad ] = React . useState ( false ) ;
1200+ React . useLayoutEffect ( ( ) => {
1201+ Scheduler . unstable_yieldValue ( 'layout create' ) ;
1202+ // Mimic an XHR request with a complete handler that updates state.
1203+ completePendingRequest = ( ) => setDidLoad ( true ) ;
1204+ return ( ) => {
1205+ Scheduler . unstable_yieldValue ( 'layout destroy' ) ;
1206+ } ;
1207+ } , [ ] ) ;
1208+ return didLoad ;
1209+ }
1210+
1211+ act ( ( ) => {
1212+ ReactNoop . renderToRootWithID ( < Component /> , 'root' , ( ) =>
1213+ Scheduler . unstable_yieldValue ( 'Sync effect' ) ,
1214+ ) ;
1215+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1216+ 'Component' ,
1217+ 'layout create' ,
1218+ 'Sync effect' ,
1219+ ] ) ;
1220+
1221+ // Unmount but don't process pending passive destroy function
1222+ ReactNoop . unmountRootWithID ( 'root' ) ;
1223+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'layout destroy' ] ) ;
1224+
1225+ // Simulate an XHR completing.
1226+ expect ( completePendingRequest ) . toErrorDev (
1227+ "Warning: Can't perform a React state update on an unmounted component." ,
1228+ ) ;
1229+ } ) ;
1230+ } ) ;
11471231 }
11481232
11491233 it ( 'updates have async priority' , ( ) => {
0 commit comments