diff --git a/examples/sdk/react/src/App.tsx b/examples/sdk/react/src/App.tsx index 0e75093b..e7df5897 100644 --- a/examples/sdk/react/src/App.tsx +++ b/examples/sdk/react/src/App.tsx @@ -1,9 +1,11 @@ import React, { useState } from 'react'; import './App.css'; -import { BacktraceClient } from '@backtrace/react'; +import { BacktraceClient, ErrorBoundary } from '@backtrace/react'; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import { SUBMISSION_URL } from './consts'; +import InnerFallback from './components/InnerFallback'; +import ButtonWithError from './components/ButtonWithError'; function App() { const [clicked, setClicked] = useState(false); @@ -50,8 +52,11 @@ function App() {

+ }> + + diff --git a/examples/sdk/react/src/components/ButtonWithError.tsx b/examples/sdk/react/src/components/ButtonWithError.tsx new file mode 100644 index 00000000..bdafa489 --- /dev/null +++ b/examples/sdk/react/src/components/ButtonWithError.tsx @@ -0,0 +1,20 @@ +import { useState } from 'react'; + +export default function ButtonWithError() { + const [clicked, setClicked] = useState(false); + + function throwOnClicked() { + if (clicked) { + throw new Error('Test throw in ButtonWithBoundary to demonstrate an Inner Error Boundary'); + } + } + + return ( + <> + {throwOnClicked()} + + + ); +} diff --git a/examples/sdk/react/src/Fallback.tsx b/examples/sdk/react/src/components/Fallback.tsx similarity index 81% rename from examples/sdk/react/src/Fallback.tsx rename to examples/sdk/react/src/components/Fallback.tsx index a7fe9fb3..103f82a3 100644 --- a/examples/sdk/react/src/Fallback.tsx +++ b/examples/sdk/react/src/components/Fallback.tsx @@ -1,6 +1,6 @@ -import './App.css'; +import '../App.css'; -export function Fallback() { +export default function Fallback() { return (
@@ -10,7 +10,8 @@ export function Fallback() { alt="Sauce Labs" />

- This is the fallback component that gets rendered after a rendering error! + This is the fallback component that gets rendered after a rendering error within the main + ErrorBoundary!

Check your Backtrace console to see the Error and Component stacks!

diff --git a/examples/sdk/react/src/components/InnerFallback.tsx b/examples/sdk/react/src/components/InnerFallback.tsx new file mode 100644 index 00000000..4c46abd7 --- /dev/null +++ b/examples/sdk/react/src/components/InnerFallback.tsx @@ -0,0 +1,9 @@ +import { useEffect } from 'react'; +import { toast } from 'react-toastify'; + +export default function InnerFallback() { + useEffect(() => { + toast('Inner ErrorBoundary Triggered! Check your Backtrace console to see the Error and Component stacks.'); + }); + return null; +} diff --git a/examples/sdk/react/src/index.tsx b/examples/sdk/react/src/index.tsx index fdcf8b84..f3724af1 100644 --- a/examples/sdk/react/src/index.tsx +++ b/examples/sdk/react/src/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { ErrorBoundary, BacktraceClient } from '@backtrace/react'; -import { Fallback } from './Fallback'; +import Fallback from './components/Fallback'; import { SUBMISSION_URL } from './consts'; BacktraceClient.initialize({ @@ -21,7 +21,7 @@ BacktraceClient.initialize({ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( - }> + }> , diff --git a/package-lock.json b/package-lock.json index d87c04f2..084d0146 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10431,9 +10431,10 @@ } }, "node_modules/ts-loader": { - "version": "9.4.3", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", + "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -11230,8 +11231,9 @@ } }, "node_modules/webpack": { - "version": "5.87.0", - "license": "MIT", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -11770,8 +11772,9 @@ }, "node_modules/webpack-cli": { "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -12272,10 +12275,13 @@ "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "ts-jest": "^29.1.1", - "typescript": "^5.0.4" + "ts-loader": "^9.4.4", + "typescript": "^5.0.4", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" }, "peerDependencies": { - "react": ">=16.14.0" + "react": ">=16.8.0" } }, "packages/sdk-core": { @@ -12845,7 +12851,10 @@ "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "ts-jest": "^29.1.1", - "typescript": "^5.0.4" + "ts-loader": "^9.4.4", + "typescript": "^5.0.4", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" } }, "@backtrace/rollup-plugin": { @@ -19357,7 +19366,9 @@ } }, "ts-loader": { - "version": "9.4.3", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", + "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -19881,7 +19892,9 @@ "dev": true }, "webpack": { - "version": "5.87.0", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "requires": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -20274,6 +20287,8 @@ }, "webpack-cli": { "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", diff --git a/packages/react/package.json b/packages/react/package.json index c4a3f42c..be402d5d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -37,9 +37,12 @@ "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "ts-jest": "^29.1.1", - "typescript": "^5.0.4" + "ts-loader": "^9.4.4", + "typescript": "^5.0.4", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" }, "peerDependencies": { - "react": ">=16.14.0" + "react": ">=16.8.0" } } diff --git a/packages/react/src/ErrorBoundary.tsx b/packages/react/src/ErrorBoundary.tsx index 2348a6fc..0e6bfb14 100644 --- a/packages/react/src/ErrorBoundary.tsx +++ b/packages/react/src/ErrorBoundary.tsx @@ -7,6 +7,7 @@ type RenderFallback = () => ReactElement; export interface Props { children: ReactNode; fallback?: ReactElement | RenderFallback; + name?: string; } export interface State { @@ -32,7 +33,11 @@ export class ErrorBoundary extends Component { } public async componentDidCatch(error: Error, info: ErrorInfo) { - const report = new BacktraceReport(error); + const { name } = this.props; + const report = new BacktraceReport(error, { + 'errorboundary.name': name ?? 'main', + 'error.type': 'Unhandled exception', + }); report.addStackTrace(this.COMPONENT_THREAD_NAME, info.componentStack); await this._client.send(report); }