1237
1238
// @gate enableAsyncActions
1239
test('useActionState: error handling (sync action)', async () => {
1240
- let resetErrorBoundary;
1240
class ErrorBoundary extends React.Component {
1241
state = {error: null};
1242
static getDerivedStateFromError(error) {
1243
return {error};
1244
}
1245
render() {
1247
- resetErrorBoundary = () => this.setState({error: null});
1246
if (this.state.error !== null) {
1247
return <Text text={'Caught an error: ' + this.state.error.message} />;
1248
}
1282
'Caught an error: Oops!',
1283
]);
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');
1285
});
1286
1287
// @gate enableAsyncActions
1288
test('useActionState: error handling (async action)', async () => {
1304
- let resetErrorBoundary;
1289
class ErrorBoundary extends React.Component {
1290
state = {error: null};
1291
static getDerivedStateFromError(error) {
1292
return {error};
1293
}
1294
render() {
1311
- resetErrorBoundary = () => this.setState({error: null});
1295
if (this.state.error !== null) {
1296
return <Text text={'Caught an error: ' + this.state.error.message} />;
1297
}
1329
await act(() => resolveText('Oops!'));
1330
assertLog(['Caught an error: Oops!', 'Caught an error: Oops!']);
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
+ }
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
+ );
1371
assertLog(['A']);
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!');
1391
});
1392
1393
// @gate enableAsyncActions