@honeybadger-io/nextjs 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/copy-config-files.d.ts +8 -1
- package/dist/copy-config-files.js +67 -11
- package/dist/copy-config-files.js.map +1 -1
- package/package.json +3 -3
- package/src/copy-config-files.ts +86 -12
- package/templates/_error_app_router.js +38 -0
- package/templates/_error_app_router.tsx +41 -0
package/README.md
CHANGED
|
@@ -31,13 +31,17 @@ This version is considered suitable for preview.
|
|
|
31
31
|
|
|
32
32
|
The following limitations are known to exist and will be tackled in future releases:
|
|
33
33
|
|
|
34
|
-
- [Issue link](https://github.com/honeybadger-io/honeybadger-js/issues/1055): A custom
|
|
34
|
+
- [Issue link](https://github.com/honeybadger-io/honeybadger-js/issues/1055): A custom error component is used to report uncaught exceptions to Honeybadger.
|
|
35
35
|
This is necessary because Next.js does not provide a way to hook into the error handler.
|
|
36
|
-
This is not a catch-all errors solution.
|
|
36
|
+
This is not a catch-all errors solution.
|
|
37
|
+
If you are using the _Pages Router_, there are some caveats to this approach, as reported [here](https://nextjs.org/docs/advanced-features/custom-error-page#caveats).
|
|
37
38
|
This is a limitation of Next.js, not Honeybadger's Next.js integration.
|
|
38
|
-
Errors thrown in middlewares or API routes will not be reported to Honeybadger, since when they reach
|
|
39
|
-
Additionally, there is an open [issue](https://github.com/vercel/next.js/issues/45535) about 404 being reported with Next.js apps deployed on Vercel, when they should be reported as 500.
|
|
40
|
-
|
|
39
|
+
Errors thrown in middlewares or API routes will not be reported to Honeybadger, since when they reach the error component, the response status code is 404 and no error information is available.
|
|
40
|
+
Additionally, there is an open [issue](https://github.com/vercel/next.js/issues/45535) about 404 being reported with Next.js apps deployed on Vercel, when they should be reported as 500.
|
|
41
|
+
If you are using the _App Router_, these limitations do not apply, because errors thrown in middlewares or API routes do not reach the custom error component
|
|
42
|
+
but are caught by the global `window.onerror` handler. However, some other server errors (i.e. from data fetching methods) will be reported with minimal information,
|
|
43
|
+
since Next.js will send a [generic error message](https://nextjs.org/docs/app/building-your-application/routing/error-handling#handling-server-errors) to this component for better security.
|
|
44
|
+
- [Issue link](https://github.com/honeybadger-io/honeybadger-js/issues/1056): Source maps for the [Edge runtime](https://vercel.com/docs/concepts/functions/edge-functions/edge-runtime) are not supported yet.
|
|
41
45
|
|
|
42
46
|
## Example app
|
|
43
47
|
|
|
@@ -2,5 +2,12 @@
|
|
|
2
2
|
declare const path: any;
|
|
3
3
|
declare const fs: any;
|
|
4
4
|
declare const debug: boolean;
|
|
5
|
-
declare function
|
|
5
|
+
declare function usesTypescript(): any;
|
|
6
|
+
declare function usesPagesRouter(): any;
|
|
7
|
+
declare function usesAppRouter(): any;
|
|
8
|
+
declare function getTargetPath(isAppRouter?: boolean, isGlobalErrorComponent?: boolean): any;
|
|
9
|
+
declare function getTemplate(isAppRouter?: boolean, isGlobalErrorComponent?: boolean): any;
|
|
10
|
+
declare function copyErrorJs(isAppRouter?: boolean): Promise<any>;
|
|
11
|
+
declare function copyGlobalErrorJs(): Promise<any>;
|
|
12
|
+
declare function copyFileWithBackup(sourcePath: any, targetPath: any): Promise<any>;
|
|
6
13
|
declare function copyConfigFiles(): Promise<void>;
|
|
@@ -3,16 +3,61 @@
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const debug = process.env.HONEYBADGER_DEBUG === 'true';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
function usesTypescript() {
|
|
7
|
+
return fs.existsSync('tsconfig.json');
|
|
8
|
+
}
|
|
9
|
+
function usesPagesRouter() {
|
|
10
|
+
return fs.existsSync('pages');
|
|
11
|
+
}
|
|
12
|
+
function usesAppRouter() {
|
|
13
|
+
return fs.existsSync('app');
|
|
14
|
+
}
|
|
15
|
+
function getTargetPath(isAppRouter = false, isGlobalErrorComponent = false) {
|
|
16
|
+
if (!isAppRouter && isGlobalErrorComponent) {
|
|
17
|
+
throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true');
|
|
18
|
+
}
|
|
19
|
+
const extension = usesTypescript() ? 'tsx' : 'js';
|
|
20
|
+
const srcFolder = isAppRouter ? 'app' : 'pages';
|
|
21
|
+
let fileName = '';
|
|
22
|
+
if (isAppRouter) {
|
|
23
|
+
fileName = isGlobalErrorComponent ? 'global-error' : 'error';
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
fileName = '_error';
|
|
27
|
+
}
|
|
28
|
+
return path.join(srcFolder, fileName + '.' + extension);
|
|
29
|
+
}
|
|
30
|
+
function getTemplate(isAppRouter = false, isGlobalErrorComponent = false) {
|
|
31
|
+
if (!isAppRouter && isGlobalErrorComponent) {
|
|
32
|
+
throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true');
|
|
33
|
+
}
|
|
34
|
+
const extension = isGlobalErrorComponent ? 'tsx' : 'js';
|
|
35
|
+
const templateName = isAppRouter ? '_error_app_router' : '_error';
|
|
36
|
+
return path.resolve(__dirname, '../templates', templateName + '.' + extension);
|
|
37
|
+
}
|
|
38
|
+
async function copyErrorJs(isAppRouter = false) {
|
|
39
|
+
const sourcePath = getTemplate(isAppRouter);
|
|
40
|
+
const targetPath = getTargetPath(isAppRouter);
|
|
41
|
+
return copyFileWithBackup(sourcePath, targetPath);
|
|
42
|
+
}
|
|
43
|
+
function copyGlobalErrorJs() {
|
|
44
|
+
const sourcePath = getTemplate(true, true);
|
|
45
|
+
const targetPath = getTargetPath(true, true);
|
|
46
|
+
return copyFileWithBackup(sourcePath, targetPath);
|
|
47
|
+
}
|
|
48
|
+
async function copyFileWithBackup(sourcePath, targetPath) {
|
|
49
|
+
const fileAlreadyExists = fs.existsSync(targetPath);
|
|
50
|
+
if (fileAlreadyExists) {
|
|
51
|
+
// Don't overwrite an existing file without creating a backup first
|
|
52
|
+
const backupPath = targetPath + '.bak';
|
|
53
|
+
if (debug) {
|
|
54
|
+
console.debug('backing up', targetPath, 'to', backupPath);
|
|
55
|
+
}
|
|
13
56
|
await fs.promises.copyFile(targetPath, backupPath);
|
|
14
57
|
}
|
|
15
|
-
|
|
58
|
+
if (debug) {
|
|
59
|
+
console.debug('copying', sourcePath, 'to', targetPath);
|
|
60
|
+
}
|
|
16
61
|
return fs.promises.copyFile(sourcePath, targetPath);
|
|
17
62
|
}
|
|
18
63
|
async function copyConfigFiles() {
|
|
@@ -20,13 +65,24 @@ async function copyConfigFiles() {
|
|
|
20
65
|
console.debug('cwd', process.cwd());
|
|
21
66
|
}
|
|
22
67
|
const templateDir = path.resolve(__dirname, '../templates');
|
|
23
|
-
const
|
|
24
|
-
|
|
68
|
+
const configFiles = [
|
|
69
|
+
'honeybadger.browser.config.js',
|
|
70
|
+
'honeybadger.edge.config.js',
|
|
71
|
+
'honeybadger.server.config.js',
|
|
72
|
+
];
|
|
73
|
+
const copyPromises = configFiles.map((file) => {
|
|
25
74
|
if (debug) {
|
|
26
75
|
console.debug('copying', file);
|
|
27
76
|
}
|
|
28
|
-
return
|
|
77
|
+
return fs.promises.copyFile(path.join(templateDir, file), file);
|
|
29
78
|
});
|
|
79
|
+
if (usesPagesRouter()) {
|
|
80
|
+
copyPromises.push(copyErrorJs(false));
|
|
81
|
+
}
|
|
82
|
+
if (usesAppRouter()) {
|
|
83
|
+
copyPromises.push(copyErrorJs(true));
|
|
84
|
+
copyPromises.push(copyGlobalErrorJs());
|
|
85
|
+
}
|
|
30
86
|
await Promise.all(copyPromises);
|
|
31
87
|
console.log('Done copying config files.');
|
|
32
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-config-files.js","sourceRoot":"","sources":["../src/copy-config-files.ts"],"names":[],"mappings":";;AAEA,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAExB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,CAAA;AAEtD,
|
|
1
|
+
{"version":3,"file":"copy-config-files.js","sourceRoot":"","sources":["../src/copy-config-files.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nconst path = require('path')\nconst fs = require('fs')\n\nconst debug = process.env.HONEYBADGER_DEBUG === 'true'\n\nfunction usesTypescript() {\n return fs.existsSync('tsconfig.json')\n}\n\nfunction usesPagesRouter() {\n return fs.existsSync('pages')\n}\n\nfunction usesAppRouter() {\n return fs.existsSync('app')\n}\n\nfunction getTargetPath(isAppRouter = false, isGlobalErrorComponent = false) {\n if (!isAppRouter && isGlobalErrorComponent) {\n throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true')\n }\n\n const extension = usesTypescript() ? 'tsx' : 'js'\n const srcFolder = isAppRouter ? 'app' : 'pages'\n\n let fileName = ''\n if (isAppRouter) {\n fileName = isGlobalErrorComponent ? 'global-error' : 'error'\n }\n else {\n fileName = '_error'\n }\n\n return path.join(srcFolder, fileName + '.' + extension)\n}\n\nfunction getTemplate(isAppRouter = false, isGlobalErrorComponent = false) {\n if (!isAppRouter && isGlobalErrorComponent) {\n throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true')\n }\n\n const extension = isGlobalErrorComponent ? 'tsx' : 'js'\n const templateName = isAppRouter ? '_error_app_router' : '_error'\n\n return path.resolve(__dirname, '../templates', templateName + '.' + extension)\n}\n\nasync function copyErrorJs(isAppRouter = false) {\n const sourcePath = getTemplate(isAppRouter)\n const targetPath = getTargetPath(isAppRouter)\n\n return copyFileWithBackup(sourcePath, targetPath)\n}\n\nfunction copyGlobalErrorJs() {\n const sourcePath = getTemplate(true, true)\n const targetPath = getTargetPath(true, true)\n\n return copyFileWithBackup(sourcePath, targetPath)\n}\n\nasync function copyFileWithBackup(sourcePath, targetPath) {\n const fileAlreadyExists = fs.existsSync(targetPath)\n if (fileAlreadyExists) {\n // Don't overwrite an existing file without creating a backup first\n const backupPath = targetPath + '.bak'\n if (debug) {\n console.debug('backing up', targetPath, 'to', backupPath)\n }\n await fs.promises.copyFile(targetPath, backupPath)\n }\n\n if (debug) {\n console.debug('copying', sourcePath, 'to', targetPath)\n }\n\n return fs.promises.copyFile(sourcePath, targetPath)\n}\n\nasync function copyConfigFiles() {\n if (debug) {\n console.debug('cwd', process.cwd())\n }\n\n const templateDir = path.resolve(__dirname, '../templates')\n const configFiles = [\n 'honeybadger.browser.config.js',\n 'honeybadger.edge.config.js',\n 'honeybadger.server.config.js',\n ]\n\n const copyPromises = configFiles.map((file) => {\n if (debug) {\n console.debug('copying', file)\n }\n return fs.promises.copyFile(path.join(templateDir, file), file)\n })\n\n if (usesPagesRouter()) {\n copyPromises.push(copyErrorJs(false))\n }\n\n if (usesAppRouter()) {\n copyPromises.push(copyErrorJs(true))\n copyPromises.push(copyGlobalErrorJs())\n }\n\n await Promise.all(copyPromises);\n\n console.log('Done copying config files.')\n}\n\ncopyConfigFiles().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honeybadger-io/nextjs",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"description": "Next.js integration for Honeybadger",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"next": "13.x"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@honeybadger-io/js": "^6.
|
|
43
|
+
"@honeybadger-io/js": "^6.1.1",
|
|
44
44
|
"@honeybadger-io/webpack": "5.1.7"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "171888777c56def8d429703139a7003a266b4af0"
|
|
61
61
|
}
|
package/src/copy-config-files.ts
CHANGED
|
@@ -5,17 +5,76 @@ const fs = require('fs')
|
|
|
5
5
|
|
|
6
6
|
const debug = process.env.HONEYBADGER_DEBUG === 'true'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
function usesTypescript() {
|
|
9
|
+
return fs.existsSync('tsconfig.json')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function usesPagesRouter() {
|
|
13
|
+
return fs.existsSync('pages')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function usesAppRouter() {
|
|
17
|
+
return fs.existsSync('app')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getTargetPath(isAppRouter = false, isGlobalErrorComponent = false) {
|
|
21
|
+
if (!isAppRouter && isGlobalErrorComponent) {
|
|
22
|
+
throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const extension = usesTypescript() ? 'tsx' : 'js'
|
|
26
|
+
const srcFolder = isAppRouter ? 'app' : 'pages'
|
|
27
|
+
|
|
28
|
+
let fileName = ''
|
|
29
|
+
if (isAppRouter) {
|
|
30
|
+
fileName = isGlobalErrorComponent ? 'global-error' : 'error'
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
fileName = '_error'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return path.join(srcFolder, fileName + '.' + extension)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getTemplate(isAppRouter = false, isGlobalErrorComponent = false) {
|
|
40
|
+
if (!isAppRouter && isGlobalErrorComponent) {
|
|
41
|
+
throw new Error('invalid arguments: isGlobalErrorComponent can only be true when isAppRouter is true')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const extension = isGlobalErrorComponent ? 'tsx' : 'js'
|
|
45
|
+
const templateName = isAppRouter ? '_error_app_router' : '_error'
|
|
46
|
+
|
|
47
|
+
return path.resolve(__dirname, '../templates', templateName + '.' + extension)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function copyErrorJs(isAppRouter = false) {
|
|
51
|
+
const sourcePath = getTemplate(isAppRouter)
|
|
52
|
+
const targetPath = getTargetPath(isAppRouter)
|
|
53
|
+
|
|
54
|
+
return copyFileWithBackup(sourcePath, targetPath)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function copyGlobalErrorJs() {
|
|
58
|
+
const sourcePath = getTemplate(true, true)
|
|
59
|
+
const targetPath = getTargetPath(true, true)
|
|
60
|
+
|
|
61
|
+
return copyFileWithBackup(sourcePath, targetPath)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function copyFileWithBackup(sourcePath, targetPath) {
|
|
65
|
+
const fileAlreadyExists = fs.existsSync(targetPath)
|
|
66
|
+
if (fileAlreadyExists) {
|
|
67
|
+
// Don't overwrite an existing file without creating a backup first
|
|
68
|
+
const backupPath = targetPath + '.bak'
|
|
69
|
+
if (debug) {
|
|
70
|
+
console.debug('backing up', targetPath, 'to', backupPath)
|
|
71
|
+
}
|
|
15
72
|
await fs.promises.copyFile(targetPath, backupPath)
|
|
16
73
|
}
|
|
17
74
|
|
|
18
|
-
|
|
75
|
+
if (debug) {
|
|
76
|
+
console.debug('copying', sourcePath, 'to', targetPath)
|
|
77
|
+
}
|
|
19
78
|
|
|
20
79
|
return fs.promises.copyFile(sourcePath, targetPath)
|
|
21
80
|
}
|
|
@@ -26,15 +85,30 @@ async function copyConfigFiles() {
|
|
|
26
85
|
}
|
|
27
86
|
|
|
28
87
|
const templateDir = path.resolve(__dirname, '../templates')
|
|
29
|
-
const
|
|
30
|
-
|
|
88
|
+
const configFiles = [
|
|
89
|
+
'honeybadger.browser.config.js',
|
|
90
|
+
'honeybadger.edge.config.js',
|
|
91
|
+
'honeybadger.server.config.js',
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
const copyPromises = configFiles.map((file) => {
|
|
31
95
|
if (debug) {
|
|
32
96
|
console.debug('copying', file)
|
|
33
97
|
}
|
|
98
|
+
return fs.promises.copyFile(path.join(templateDir, file), file)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
if (usesPagesRouter()) {
|
|
102
|
+
copyPromises.push(copyErrorJs(false))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (usesAppRouter()) {
|
|
106
|
+
copyPromises.push(copyErrorJs(true))
|
|
107
|
+
copyPromises.push(copyGlobalErrorJs())
|
|
108
|
+
}
|
|
34
109
|
|
|
35
|
-
return file === '_error.js' ? copyErrorJs() : fs.promises.copyFile(path.join(templateDir, file), file)
|
|
36
|
-
});
|
|
37
110
|
await Promise.all(copyPromises);
|
|
111
|
+
|
|
38
112
|
console.log('Done copying config files.')
|
|
39
113
|
}
|
|
40
114
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client'; // Error components must be Client Components
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line import/no-unresolved
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import { Honeybadger } from '@honeybadger-io/react'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* error.[js|tsx]: https://nextjs.org/docs/app/building-your-application/routing/error-handling
|
|
9
|
+
* global-error.[js|tsx]: https://nextjs.org/docs/app/building-your-application/routing/error-handling#handling-errors-in-layouts
|
|
10
|
+
*
|
|
11
|
+
* This component is called when:
|
|
12
|
+
* - on the server, when data fetching methods throw or reject
|
|
13
|
+
* - on the client, when getInitialProps throws or rejects
|
|
14
|
+
* - on the client, when a React lifecycle method (render, componentDidMount, etc) throws or rejects
|
|
15
|
+
* and was caught by the built-in Next.js error boundary
|
|
16
|
+
*/
|
|
17
|
+
export default function Error({
|
|
18
|
+
error,
|
|
19
|
+
reset,
|
|
20
|
+
}) {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
Honeybadger.notify(error)
|
|
23
|
+
}, [error])
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div>
|
|
27
|
+
<h2>Something went wrong!</h2>
|
|
28
|
+
<button
|
|
29
|
+
onClick={
|
|
30
|
+
// Attempt to recover by trying to re-render the segment
|
|
31
|
+
() => reset()
|
|
32
|
+
}
|
|
33
|
+
>
|
|
34
|
+
Try again
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client'; // Error components must be Client Components
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line import/no-unresolved
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import { Honeybadger } from '@honeybadger-io/react'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* error.[js|tsx]: https://nextjs.org/docs/app/building-your-application/routing/error-handling
|
|
9
|
+
* global-error.[js|tsx]: https://nextjs.org/docs/app/building-your-application/routing/error-handling#handling-errors-in-layouts
|
|
10
|
+
*
|
|
11
|
+
* This component is called when:
|
|
12
|
+
* - on the server, when data fetching methods throw or reject
|
|
13
|
+
* - on the client, when getInitialProps throws or rejects
|
|
14
|
+
* - on the client, when a React lifecycle method (render, componentDidMount, etc) throws or rejects
|
|
15
|
+
* and was caught by the built-in Next.js error boundary
|
|
16
|
+
*/
|
|
17
|
+
export default function Error({
|
|
18
|
+
error,
|
|
19
|
+
reset,
|
|
20
|
+
}: {
|
|
21
|
+
error: Error;
|
|
22
|
+
reset: () => void;
|
|
23
|
+
}) {
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
Honeybadger.notify(error)
|
|
26
|
+
}, [error])
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
<h2>Something went wrong!</h2>
|
|
31
|
+
<button
|
|
32
|
+
onClick={
|
|
33
|
+
// Attempt to recover by trying to re-render the segment
|
|
34
|
+
() => reset()
|
|
35
|
+
}
|
|
36
|
+
>
|
|
37
|
+
Try again
|
|
38
|
+
</button>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|