@@ -1144,6 +1144,184 @@ function loadModules({
1144
1144
] ) ;
1145
1145
} ) ;
1146
1146
} ) ;
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
+ } ) ;
1231
+
1232
+ it ( 'still warns if there are pending passive unmount effects but not for the current fiber' , ( ) => {
1233
+ let completePendingRequest = null ;
1234
+ function ComponentWithXHR ( ) {
1235
+ Scheduler . unstable_yieldValue ( 'Component' ) ;
1236
+ const [ didLoad , setDidLoad ] = React . useState ( false ) ;
1237
+ React . useLayoutEffect ( ( ) => {
1238
+ Scheduler . unstable_yieldValue ( 'a:layout create' ) ;
1239
+ return ( ) => {
1240
+ Scheduler . unstable_yieldValue ( 'a:layout destroy' ) ;
1241
+ } ;
1242
+ } , [ ] ) ;
1243
+ React . useEffect ( ( ) => {
1244
+ Scheduler . unstable_yieldValue ( 'a:passive create' ) ;
1245
+ // Mimic an XHR request with a complete handler that updates state.
1246
+ completePendingRequest = ( ) => setDidLoad ( true ) ;
1247
+ } , [ ] ) ;
1248
+ return didLoad ;
1249
+ }
1250
+
1251
+ function ComponentWithPendingPassiveUnmount ( ) {
1252
+ React . useEffect ( ( ) => {
1253
+ Scheduler . unstable_yieldValue ( 'b:passive create' ) ;
1254
+ return ( ) => {
1255
+ Scheduler . unstable_yieldValue ( 'b:passive destroy' ) ;
1256
+ } ;
1257
+ } , [ ] ) ;
1258
+ return null ;
1259
+ }
1260
+
1261
+ act ( ( ) => {
1262
+ ReactNoop . renderToRootWithID (
1263
+ < >
1264
+ < ComponentWithXHR />
1265
+ < ComponentWithPendingPassiveUnmount />
1266
+ </ > ,
1267
+ 'root' ,
1268
+ ( ) => Scheduler . unstable_yieldValue ( 'Sync effect' ) ,
1269
+ ) ;
1270
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1271
+ 'Component' ,
1272
+ 'a:layout create' ,
1273
+ 'Sync effect' ,
1274
+ ] ) ;
1275
+ ReactNoop . flushPassiveEffects ( ) ;
1276
+ expect ( Scheduler ) . toHaveYielded ( [
1277
+ 'a:passive create' ,
1278
+ 'b:passive create' ,
1279
+ ] ) ;
1280
+
1281
+ // Unmount but don't process pending passive destroy function
1282
+ ReactNoop . unmountRootWithID ( 'root' ) ;
1283
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'a:layout destroy' ] ) ;
1284
+
1285
+ // Simulate an XHR completing in the component without a pending passive effect..
1286
+ expect ( completePendingRequest ) . toErrorDev (
1287
+ "Warning: Can't perform a React state update on an unmounted component." ,
1288
+ ) ;
1289
+ } ) ;
1290
+ } ) ;
1291
+
1292
+ it ( 'still warns about state updates from within passive unmount function' , ( ) => {
1293
+ function Component ( ) {
1294
+ Scheduler . unstable_yieldValue ( 'Component' ) ;
1295
+ const [ didLoad , setDidLoad ] = React . useState ( false ) ;
1296
+ React . useEffect ( ( ) => {
1297
+ Scheduler . unstable_yieldValue ( 'passive create' ) ;
1298
+ return ( ) => {
1299
+ setDidLoad ( true ) ;
1300
+ Scheduler . unstable_yieldValue ( 'passive destroy' ) ;
1301
+ } ;
1302
+ } , [ ] ) ;
1303
+ return didLoad ;
1304
+ }
1305
+
1306
+ act ( ( ) => {
1307
+ ReactNoop . renderToRootWithID ( < Component /> , 'root' , ( ) =>
1308
+ Scheduler . unstable_yieldValue ( 'Sync effect' ) ,
1309
+ ) ;
1310
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
1311
+ 'Component' ,
1312
+ 'Sync effect' ,
1313
+ 'passive create' ,
1314
+ ] ) ;
1315
+
1316
+ // Unmount but don't process pending passive destroy function
1317
+ ReactNoop . unmountRootWithID ( 'root' ) ;
1318
+ expect ( ( ) => {
1319
+ expect ( Scheduler ) . toFlushAndYield ( [ 'passive destroy' ] ) ;
1320
+ } ) . toErrorDev (
1321
+ "Warning: Can't perform a React state update on an unmounted component." ,
1322
+ ) ;
1323
+ } ) ;
1324
+ } ) ;
1147
1325
}
1148
1326
1149
1327
it ( 'updates have async priority' , ( ) => {
0 commit comments