Add error boundary to Flight fixture (#26695)
Errors in form actions are now rethrown during render (#26689), so we can handle them using an error boundary.
Andrew Clark committed
Apr 21, 2023 at 14:23 UTC
967d46c76cf9f7f35cf659a6a47c9ad456c685a8
3 files changed
+54
-32
fixtures/flight/src/Button.js
+20
-17
@@ -1,27 +1,30 @@
1
'use client';
2
3
import * as React from 'react';
4
+import {flushSync} from 'react-dom';
5
+import ErrorBoundary from './ErrorBoundary.js';
6
7
export default function Button({action, children}) {
8
const [isPending, setIsPending] = React.useState(false);
9
10
return (
9
- <form>
10
- <button
11
- disabled={isPending}
12
- formAction={async () => {
13
- setIsPending(true);
14
- try {
15
- const result = await action();
16
- console.log(result);
17
- } catch (error) {
18
- console.error(error);
19
- } finally {
20
- setIsPending(false);
21
- }
22
- }}>
23
- {children}
24
- </button>
25
- </form>
11
+ <ErrorBoundary>
12
+ <form>
13
+ <button
14
+ disabled={isPending}
15
+ formAction={async () => {
16
+ // TODO: Migrate to useFormPending once that exists
17
+ flushSync(() => setIsPending(true));
18
+ try {
19
+ const result = await action();
20
+ console.log(result);
21
+ } finally {
22
+ React.startTransition(() => setIsPending(false));
23
+ }
24
+ }}>
25
+ {children}
26
+ </button>
27
+ </form>
28
+ </ErrorBoundary>
29
);
30
}
fixtures/flight/src/ErrorBoundary.js
new
+16
@@ -0,0 +1,16 @@
1
+'use client';
2
+
3
+import * as React from 'react';
4
+
5
+export default class ErrorBoundary extends React.Component {
6
+ state = {error: null};
7
+ static getDerivedStateFromError(error) {
8
+ return {error};
9
+ }
10
+ render() {
11
+ if (this.state.error) {
12
+ return <div>Caught an error: {this.state.error.message}</div>;
13
+ }
14
+ return this.props.children;
15
+ }
16
+}
fixtures/flight/src/Form.js
+18
-15
@@ -1,25 +1,28 @@
1
'use client';
2
3
import * as React from 'react';
4
+import {flushSync} from 'react-dom';
5
+import ErrorBoundary from './ErrorBoundary.js';
6
7
export default function Form({action, children}) {
8
const [isPending, setIsPending] = React.useState(false);
9
10
return (
9
- <form
10
- action={async formData => {
11
- setIsPending(true);
12
- try {
13
- const result = await action(formData);
14
- alert(result);
15
- } catch (error) {
16
- console.error(error);
17
- } finally {
18
- setIsPending(false);
19
- }
20
- }}>
21
- <input name="name" />
22
- <button>Say Hi</button>
23
- </form>
11
+ <ErrorBoundary>
12
+ <form
13
+ action={async formData => {
14
+ // TODO: Migrate to useFormPending once that exists
15
+ flushSync(() => setIsPending(true));
16
+ try {
17
+ const result = await action(formData);
18
+ alert(result);
19
+ } finally {
20
+ React.startTransition(() => setIsPending(false));
21
+ }
22
+ }}>
23
+ <input name="name" />
24
+ <button>Say Hi</button>
25
+ </form>
26
+ </ErrorBoundary>
27
);
28
}