@@ -1237,14 +1237,12 @@ describe('ReactDOMForm', () => {
1237
1237
1238
1238
// @gate enableAsyncActions
1239
1239
test ( 'useActionState: error handling (sync action)' , async ( ) => {
1240
- let resetErrorBoundary ;
1241
1240
class ErrorBoundary extends React . Component {
1242
1241
state = { error : null } ;
1243
1242
static getDerivedStateFromError ( error ) {
1244
1243
return { error} ;
1245
1244
}
1246
1245
render ( ) {
1247
- resetErrorBoundary = ( ) => this . setState ( { error : null } ) ;
1248
1246
if ( this . state . error !== null ) {
1249
1247
return < Text text = { 'Caught an error: ' + this . state . error . message } /> ;
1250
1248
}
@@ -1284,31 +1282,16 @@ describe('ReactDOMForm', () => {
1284
1282
'Caught an error: Oops!' ,
1285
1283
] ) ;
1286
1284
expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
1287
-
1288
- // Reset the error boundary
1289
- await act ( ( ) => resetErrorBoundary ( ) ) ;
1290
- assertLog ( [ 'A' ] ) ;
1291
-
1292
- // Trigger an error again, but this time, perform another action that
1293
- // overrides the first one and fixes the error
1294
- await act ( ( ) => {
1295
- startTransition ( ( ) => action ( 'Oops!' ) ) ;
1296
- startTransition ( ( ) => action ( 'B' ) ) ;
1297
- } ) ;
1298
- assertLog ( [ 'Pending A' , 'B' ] ) ;
1299
- expect ( container . textContent ) . toBe ( 'B' ) ;
1300
1285
} ) ;
1301
1286
1302
1287
// @gate enableAsyncActions
1303
1288
test ( 'useActionState: error handling (async action)' , async ( ) => {
1304
- let resetErrorBoundary ;
1305
1289
class ErrorBoundary extends React . Component {
1306
1290
state = { error : null } ;
1307
1291
static getDerivedStateFromError ( error ) {
1308
1292
return { error} ;
1309
1293
}
1310
1294
render ( ) {
1311
- resetErrorBoundary = ( ) => this . setState ( { error : null } ) ;
1312
1295
if ( this . state . error !== null ) {
1313
1296
return < Text text = { 'Caught an error: ' + this . state . error . message } /> ;
1314
1297
}
@@ -1346,21 +1329,65 @@ describe('ReactDOMForm', () => {
1346
1329
await act ( ( ) => resolveText ( 'Oops!' ) ) ;
1347
1330
assertLog ( [ 'Caught an error: Oops!' , 'Caught an error: Oops!' ] ) ;
1348
1331
expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
1332
+ } ) ;
1333
+
1334
+ test ( 'useActionState: when an action errors, subsequent actions are canceled' , async ( ) => {
1335
+ class ErrorBoundary extends React . Component {
1336
+ state = { error : null } ;
1337
+ static getDerivedStateFromError ( error ) {
1338
+ return { error} ;
1339
+ }
1340
+ render ( ) {
1341
+ if ( this . state . error !== null ) {
1342
+ return < Text text = { 'Caught an error: ' + this . state . error . message } /> ;
1343
+ }
1344
+ return this . props . children ;
1345
+ }
1346
+ }
1347
+
1348
+ let action ;
1349
+ function App ( ) {
1350
+ const [ state , dispatch , isPending ] = useActionState ( async ( s , a ) => {
1351
+ Scheduler . log ( 'Start action: ' + a ) ;
1352
+ const text = await getText ( a ) ;
1353
+ if ( text . endsWith ( '!' ) ) {
1354
+ throw new Error ( text ) ;
1355
+ }
1356
+ return text ;
1357
+ } , 'A' ) ;
1358
+ action = dispatch ;
1359
+ const pending = isPending ? 'Pending ' : '' ;
1360
+ return < Text text = { pending + state } /> ;
1361
+ }
1349
1362
1350
- // Reset the error boundary
1351
- await act ( ( ) => resetErrorBoundary ( ) ) ;
1363
+ const root = ReactDOMClient . createRoot ( container ) ;
1364
+ await act ( ( ) =>
1365
+ root . render (
1366
+ < ErrorBoundary >
1367
+ < App />
1368
+ </ ErrorBoundary > ,
1369
+ ) ,
1370
+ ) ;
1352
1371
assertLog ( [ 'A' ] ) ;
1353
1372
1354
- // Trigger an error again, but this time, perform another action that
1355
- // overrides the first one and fixes the error
1356
- await act ( ( ) => {
1357
- startTransition ( ( ) => action ( 'Oops!' ) ) ;
1358
- startTransition ( ( ) => action ( 'B' ) ) ;
1359
- } ) ;
1360
- assertLog ( [ 'Pending A' ] ) ;
1361
- await act ( ( ) => resolveText ( 'B' ) ) ;
1362
- assertLog ( [ 'B' ] ) ;
1363
- expect ( container . textContent ) . toBe ( 'B' ) ;
1373
+ await act ( ( ) => startTransition ( ( ) => action ( 'Oops!' ) ) ) ;
1374
+ assertLog ( [ 'Start action: Oops!' , 'Pending A' ] ) ;
1375
+
1376
+ // Queue up another action after the one will error.
1377
+ await act ( ( ) => startTransition ( ( ) => action ( 'Should never run' ) ) ) ;
1378
+ assertLog ( [ ] ) ;
1379
+
1380
+ // The first dispatch will update the pending state.
1381
+ await act ( ( ) => resolveText ( 'Oops!' ) ) ;
1382
+ assertLog ( [ 'Caught an error: Oops!' , 'Caught an error: Oops!' ] ) ;
1383
+ expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
1384
+
1385
+ // Attempt to dispatch another action. This should not run either.
1386
+ await act ( ( ) =>
1387
+ startTransition ( ( ) => action ( 'This also should never run' ) ) ,
1388
+ ) ;
1389
+ assertLog ( [ ] ) ;
1390
+ expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
1364
1391
} ) ;
1365
1392
1366
1393
// @gate enableAsyncActions
0 commit comments