Add error boundaries and a default one with notifications.
This commit is contained in:
parent
a1b0cc816c
commit
cbedfa9d52
6 changed files with 111 additions and 3 deletions
|
@ -36,6 +36,8 @@ import {useNotify} from "../src/Components/Notifications/Notifications";
|
|||
import {Notification, NotificationType} from "../src/Components/Notifications/Notification";
|
||||
import {Box} from "../src/Components/Box";
|
||||
import {Await, useAsync} from "../src/Async";
|
||||
import {NotifyErrorsBoundary} from "../src/Components/Errors/NotifyErrorsBoundary";
|
||||
import {DemoFailingComponent, DemoResetComponent} from "./DemoFailingComponent";
|
||||
|
||||
export function DemoApp()
|
||||
{
|
||||
|
@ -68,6 +70,8 @@ export function DemoApp()
|
|||
}, 2000);
|
||||
}), [asyncChange]);
|
||||
|
||||
const [failingComponentsCount, setFailingComponentsCount] = useState(0);
|
||||
|
||||
return (
|
||||
<Application>
|
||||
<MainMenu>
|
||||
|
@ -584,8 +588,20 @@ export function DemoApp()
|
|||
<p>Data: {data}</p>
|
||||
)}
|
||||
</Await>
|
||||
<button type={"button"} onClick={() => setAsyncChange(asyncChange + 1)}>Change</button>
|
||||
<button type={"button"} onClick={() => setAnotherChange(anotherChange + 1)}>Another change</button>
|
||||
<button type={"button"} onClick={() => setAsyncChange(asyncChange + 1)}>Change async deps</button>
|
||||
<button type={"button"} onClick={() => setAnotherChange(anotherChange + 1)}>Change something else</button>
|
||||
</Card>
|
||||
|
||||
<h2>Error boundaries</h2>
|
||||
|
||||
<Card>
|
||||
<button type={"button"} className={"error"}
|
||||
onClick={() => setFailingComponentsCount(failingComponentsCount + 1)}>Do something dangerous</button>
|
||||
<NotifyErrorsBoundary fallback={<DemoResetComponent />}>
|
||||
{
|
||||
[...Array(failingComponentsCount)].map(() => <DemoFailingComponent />)
|
||||
}
|
||||
</NotifyErrorsBoundary>
|
||||
</Card>
|
||||
|
||||
</Application>
|
||||
|
|
26
demo/DemoFailingComponent.tsx
Normal file
26
demo/DemoFailingComponent.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from "react";
|
||||
import {useErrorBoundary} from "react-error-boundary";
|
||||
|
||||
/**
|
||||
* A simple demo failing component.
|
||||
*/
|
||||
export function DemoFailingComponent()
|
||||
{
|
||||
throw new Error("Proudly thrown error.");
|
||||
return (<p>I will never be shown...</p>);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple demo reset component.
|
||||
*/
|
||||
export function DemoResetComponent()
|
||||
{
|
||||
// Get error boundary.
|
||||
const errorBoundary = useErrorBoundary();
|
||||
|
||||
return (
|
||||
<button type={"button"} onClick={() => {
|
||||
errorBoundary.resetBoundary();
|
||||
}}>Reset to try again!</button>
|
||||
);
|
||||
}
|
1
index.ts
1
index.ts
|
@ -12,6 +12,7 @@ export {useCurtain} from "./src/Components/Curtains/CurtainInstance";
|
|||
export type {CurtainContextState} from "./src/Components/Curtains/CurtainInstance";
|
||||
export * from "./src/Components/Dates/Calendar";
|
||||
export * from "./src/Components/Dates/Datepicker";
|
||||
export * from "./src/Components/Errors/NotifyErrorsBoundary";
|
||||
export * from "./src/Components/Floating/Float";
|
||||
export * from "./src/Components/Floating/Tooltip";
|
||||
export * from "./src/Components/Forms/Checkbox";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"name": "@kernelui/core",
|
||||
"description": "Kernel UI Core.",
|
||||
"scripts": {
|
||||
|
@ -21,6 +21,7 @@
|
|||
"@fontsource-variable/jetbrains-mono": "^5.0.21",
|
||||
"@fontsource-variable/manrope": "^5.0.20",
|
||||
"@fontsource-variable/source-serif-4": "^5.0.19",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-merge-refs": "^2.1.1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
|
|
36
src/Components/Errors/NotifyErrorsBoundary.tsx
Normal file
36
src/Components/Errors/NotifyErrorsBoundary.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React, {useCallback, useMemo} from "react";
|
||||
import {ErrorBoundary, ErrorBoundaryProps} from "react-error-boundary";
|
||||
import {useNotify} from "../Notifications/Notifications";
|
||||
import {Notification, NotificationType} from "../Notifications/Notification";
|
||||
|
||||
/**
|
||||
* A React error boundary that show errors in notifications.
|
||||
*/
|
||||
export function NotifyErrorsBoundary({children, onError, fallback, fallbackRender, FallbackComponent, ...props}: React.PropsWithChildren<Partial<ErrorBoundaryProps>>)
|
||||
{
|
||||
// Get notification function.
|
||||
const notify = useNotify();
|
||||
|
||||
/**
|
||||
* Error handling function.
|
||||
*/
|
||||
const handleError = useCallback((error: Error, info: React.ErrorInfo) => {
|
||||
// Show a notification about the error.
|
||||
notify(<Notification type={NotificationType.ERROR}>Unexpected error: {error.message}</Notification>);
|
||||
// Then call defined onError, if there is one.
|
||||
onError?.(error, info);
|
||||
}, [onError]);
|
||||
|
||||
// Define default fallback component.
|
||||
const defaultFallback = useMemo(() => <></>, []);
|
||||
|
||||
if (!fallback && !fallbackRender && !FallbackComponent)
|
||||
// Set default fallback if nothing is set.
|
||||
fallback = defaultFallback;
|
||||
|
||||
return (
|
||||
<ErrorBoundary onError={handleError} fallback={fallback} {...props}>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
28
yarn.lock
28
yarn.lock
|
@ -229,6 +229,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.12.5":
|
||||
version: 7.25.6
|
||||
resolution: "@babel/runtime@npm:7.25.6"
|
||||
dependencies:
|
||||
regenerator-runtime: "npm:^0.14.0"
|
||||
checksum: 10c0/d6143adf5aa1ce79ed374e33fdfd74fa975055a80bc6e479672ab1eadc4e4bfd7484444e17dd063a1d180e051f3ec62b357c7a2b817e7657687b47313158c3d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/template@npm:^7.24.6":
|
||||
version: 7.24.6
|
||||
resolution: "@babel/template@npm:7.24.6"
|
||||
|
@ -576,6 +585,7 @@ __metadata:
|
|||
less: "npm:^4.2.0"
|
||||
react: "npm:^18.3.1"
|
||||
react-dom: "npm:^18.3.1"
|
||||
react-error-boundary: "npm:^4.0.13"
|
||||
react-merge-refs: "npm:^2.1.1"
|
||||
react-router-dom: "npm:^6.24.1"
|
||||
typescript: "npm:^5.4.5"
|
||||
|
@ -2331,6 +2341,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-error-boundary@npm:^4.0.13":
|
||||
version: 4.0.13
|
||||
resolution: "react-error-boundary@npm:4.0.13"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.12.5"
|
||||
peerDependencies:
|
||||
react: ">=16.13.1"
|
||||
checksum: 10c0/6f3e0e4d7669f680ccf49c08c9571519c6e31f04dcfc30a765a7136c7e6fbbbe93423dd5a9fce12107f8166e54133e9dd5c2079a00c7a38201ac811f7a28b8e7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-merge-refs@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "react-merge-refs@npm:2.1.1"
|
||||
|
@ -2378,6 +2399,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"regenerator-runtime@npm:^0.14.0":
|
||||
version: 0.14.1
|
||||
resolution: "regenerator-runtime@npm:0.14.1"
|
||||
checksum: 10c0/1b16eb2c4bceb1665c89de70dcb64126a22bc8eb958feef3cd68fe11ac6d2a4899b5cd1b80b0774c7c03591dc57d16631a7f69d2daa2ec98100e2f29f7ec4cc4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve@npm:~1.19.0":
|
||||
version: 1.19.0
|
||||
resolution: "resolve@npm:1.19.0"
|
||||
|
|
Loading…
Reference in a new issue