From 5cc2ef02cec0b31c5d449e189a054fbef5801f60 Mon Sep 17 00:00:00 2001 From: zyachel Date: Sun, 22 Jan 2023 21:14:46 +0530 Subject: [PATCH] feat: add error boundary makes crashes graceful --- src/components/error/ErrorBoundary.tsx | 45 ++++++++++++++ src/components/error/ErrorInfo.tsx | 58 ++++++++++++------- src/pages/404.tsx | 2 +- src/pages/500.tsx | 2 +- src/pages/_app.tsx | 12 ++-- src/styles/base/_typography.scss | 1 + .../components/error/error-info.module.scss | 12 +++- 7 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 src/components/error/ErrorBoundary.tsx diff --git a/src/components/error/ErrorBoundary.tsx b/src/components/error/ErrorBoundary.tsx new file mode 100644 index 0000000..9c9e182 --- /dev/null +++ b/src/components/error/ErrorBoundary.tsx @@ -0,0 +1,45 @@ +import React, { Component, ErrorInfo, ReactNode } from 'react'; +import ErrorInfoComponent from './ErrorInfo'; + +type Props = { + children: ReactNode; +}; +type State = { + hasError: boolean; +}; + +class ErrorBoundary extends Component { + state: State = { + hasError: false, + }; + + static getDerivedStateFromError(error: Error): State { + return { hasError: true }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('Uncaught error:', error, errorInfo); + } + + resetError() { + this.setState({ hasError: false }); + } + + render() { + if (this.state.hasError) + return ( + + ); + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/src/components/error/ErrorInfo.tsx b/src/components/error/ErrorInfo.tsx index cfc328b..08f17b4 100644 --- a/src/components/error/ErrorInfo.tsx +++ b/src/components/error/ErrorInfo.tsx @@ -1,3 +1,4 @@ +import { ReactNode } from 'react'; import Link from 'next/link'; import Layout from '../../layouts/Layout'; import Meta from '../meta/Meta'; @@ -8,39 +9,56 @@ import styles from '../../styles/modules/components/error/error-info.module.scss // description copied verbatim from https://www.gnu.org/graphics/sventsitsky-sadgnu.html // 404 idea from ninamori.org 404 page. -const ErrorInfo = ({ message = 'Not found, sorry.', statusCode = 404 }) => { +type Props = { + message: string; + statusCode?: number; + // props specific to error boundary. + misc?: { + subtext: string; + buttonText: string; + buttonClickHandler: () => void; + }; +}; + +const ErrorInfo = ({ message, statusCode, misc }: Props) => { + const title = statusCode ? `${message} (${statusCode})` : message; return ( <> - + - GNU and Tux - + GNU and Tux + A pencil drawing of a big gnu and a small penguin, both very sad. GNU is despondently sitting on a bench, and Tux stands beside him, looking down and patting him on the back. - +

- {message} - ({statusCode}) + {title}

-

- Go back to{' '} - - the homepage - - . -

+ {misc ? ( + <> +

{misc.subtext}

+ + + ) : ( +

+ Go back to{' '} + + the homepage + + . +

+ )}
); diff --git a/src/pages/404.tsx b/src/pages/404.tsx index eb4cf2f..308ff26 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,7 +1,7 @@ import ErrorInfo from '../components/error/ErrorInfo'; const Error404 = () => { - return ; + return ; }; export default Error404; diff --git a/src/pages/500.tsx b/src/pages/500.tsx index 2b5d239..f8907a3 100644 --- a/src/pages/500.tsx +++ b/src/pages/500.tsx @@ -1,6 +1,6 @@ import ErrorInfo from '../components/error/ErrorInfo'; const Error500 = () => { - return ; + return ; }; export default Error500; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6948672..fd73f63 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,10 +1,10 @@ import type { AppProps } from 'next/app'; import usePageLoading from '../hooks/usePageLoading'; import ProgressBar from '../components/loaders/ProgressBar'; +import ErrorBoundary from '../components/error/ErrorBoundary'; import ThemeProvider from '../context/theme-context'; import '../styles/main.scss'; -import { useRouter } from 'next/router'; const ModifiedApp = ({ Component, pageProps }: AppProps) => { const { isPageLoading, key } = usePageLoading(); @@ -12,10 +12,12 @@ const ModifiedApp = ({ Component, pageProps }: AppProps) => { return ( {isPageLoading && } - + + + ); }; diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss index a32709b..d0fd7cf 100644 --- a/src/styles/base/_typography.scss +++ b/src/styles/base/_typography.scss @@ -7,6 +7,7 @@ body { color: var(--clr-text-accent); font-family: var(--ff-accent); font-weight: var(--fw-medium); + line-height: 1.2; &__primary { font-size: var(--fs-1); diff --git a/src/styles/modules/components/error/error-info.module.scss b/src/styles/modules/components/error/error-info.module.scss index 2cbc75a..1cfa76b 100644 --- a/src/styles/modules/components/error/error-info.module.scss +++ b/src/styles/modules/components/error/error-info.module.scss @@ -28,6 +28,16 @@ } .heading { - // justify-self: center; text-align: center; } + +.button { + align-self: end; + + font: inherit; + cursor: pointer; + border: none; + background: transparent; + color: var(--clr-link); + border-bottom: 2px solid var(--clr-link); +}