@@ -1252,6 +1252,95 @@ describe('ReactHooksWithNoopRenderer', () => {
1252
1252
} ) ;
1253
1253
} ) ;
1254
1254
1255
+ // @gate deferPassiveEffectCleanupDuringUnmount && runAllPassiveEffectDestroysBeforeCreates
1256
+ it ( 'does not warn about state updates for unmounted components with pending passive unmounts for alternates' , ( ) => {
1257
+ let setParentState = null ;
1258
+ const setChildStates = [ ] ;
1259
+
1260
+ function Parent ( ) {
1261
+ const [ state , setState ] = useState ( true ) ;
1262
+ setParentState = setState ;
1263
+ Scheduler . unstable_yieldValue ( `Parent ${ state } render` ) ;
1264
+ useLayoutEffect ( ( ) => {
1265
+ Scheduler . unstable_yieldValue ( `Parent ${ state } commit` ) ;
1266
+ } ) ;
1267
+ if ( state ) {
1268
+ return (
1269
+ < >
1270
+ < Child label = "one" />
1271
+ < Child label = "two" />
1272
+ </ >
1273
+ ) ;
1274
+ } else {
1275
+ return null ;
1276
+ }
1277
+ }
1278
+
1279
+ function Child ( { label} ) {
1280
+ const [ state , setState ] = useState ( 0 ) ;
1281
+ useLayoutEffect ( ( ) => {
1282
+ Scheduler . unstable_yieldValue ( `Child ${ label } commit` ) ;
1283
+ } ) ;
1284
+ useEffect ( ( ) => {
1285
+ setChildStates . push ( setState ) ;
1286
+ Scheduler . unstable_yieldValue ( `Child ${ label } passive create` ) ;
1287
+ return ( ) => {
1288
+ Scheduler . unstable_yieldValue ( `Child ${ label } passive destroy` ) ;
1289
+ } ;
1290
+ } , [ ] ) ;
1291
+ Scheduler . unstable_yieldValue ( `Child ${ label } render` ) ;
1292
+ return state ;
1293
+ }
1294
+
1295
+ // Schedule debounced state update for child (prob a no-op for this test)
1296
+ // later tick: schedule unmount for parent
1297
+ // start process unmount (but don't flush passive effectS)
1298
+ // State update on child
1299
+ act ( ( ) => {
1300
+ ReactNoop . render ( < Parent /> ) ;
1301
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1302
+ 'Parent true render' ,
1303
+ 'Child one render' ,
1304
+ 'Child two render' ,
1305
+ 'Child one commit' ,
1306
+ 'Child two commit' ,
1307
+ 'Parent true commit' ,
1308
+ 'Child one passive create' ,
1309
+ 'Child two passive create' ,
1310
+ ] ) ;
1311
+
1312
+ // Update children.
1313
+ setChildStates . forEach ( setChildState => setChildState ( 1 ) ) ;
1314
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1315
+ 'Child one render' ,
1316
+ 'Child two render' ,
1317
+ 'Child one commit' ,
1318
+ 'Child two commit' ,
1319
+ ] ) ;
1320
+
1321
+ // Schedule another update for children, and partially process it.
1322
+ setChildStates . forEach ( setChildState => setChildState ( 2 ) ) ;
1323
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'Child one render' ] ) ;
1324
+
1325
+ // Schedule unmount for the parent that unmounts children with pending update.
1326
+ Scheduler . unstable_runWithPriority (
1327
+ Scheduler . unstable_UserBlockingPriority ,
1328
+ ( ) => setParentState ( false ) ,
1329
+ ) ;
1330
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1331
+ 'Parent false render' ,
1332
+ 'Parent false commit' ,
1333
+ ] ) ;
1334
+
1335
+ // Schedule updates for children too (which should be ignored)
1336
+ setChildStates . forEach ( setChildState => setChildState ( 2 ) ) ;
1337
+ expect ( Scheduler ) . toFlushAndYield ( [
1338
+ 'Child one passive destroy' ,
1339
+ 'Child two passive destroy' ,
1340
+ ] ) ;
1341
+ } ) ;
1342
+ } ) ;
1343
+
1255
1344
it ( 'warns about state updates for unmounted components with no pending passive unmounts' , ( ) => {
1256
1345
let completePendingRequest = null ;
1257
1346
function Component ( ) {
0 commit comments