From 326ba672cd31c9ef2aed214130a87757c2dee8fc Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Wed, 10 Jan 2024 19:37:59 +0100 Subject: [PATCH] Replace `react-error-boundary` with own implementation (#26094) Closes #21515 Signed-off-by: Jon Koops --- .github/dependabot.yml | 3 - js/apps/admin-ui/package.json | 1 - js/apps/admin-ui/src/App.tsx | 47 ++++++------ .../src/components/error/ErrorRenderer.tsx | 20 ++--- .../admin-ui/src/context/ErrorBoundary.tsx | 76 +++++++++++++++++++ js/apps/admin-ui/src/utils/useFetch.ts | 7 +- js/pnpm-lock.yaml | 14 ---- 7 files changed, 112 insertions(+), 56 deletions(-) create mode 100644 js/apps/admin-ui/src/context/ErrorBoundary.tsx diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a8597c199ed..3f1406802f7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -34,6 +34,3 @@ updates: labels: - area/dependencies - team/ui - ignore: - - dependency-name: react-error-boundary - update-types: ["version-update:semver-major"] diff --git a/js/apps/admin-ui/package.json b/js/apps/admin-ui/package.json index 6282d325ea5..96170f27ec0 100644 --- a/js/apps/admin-ui/package.json +++ b/js/apps/admin-ui/package.json @@ -81,7 +81,6 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", - "react-error-boundary": "^3.1.4", "react-hook-form": "^7.49.3", "react-i18next": "^14.0.0", "react-router-dom": "^6.21.1", diff --git a/js/apps/admin-ui/src/App.tsx b/js/apps/admin-ui/src/App.tsx index d1533e3e4ed..a17a9d25ebd 100644 --- a/js/apps/admin-ui/src/App.tsx +++ b/js/apps/admin-ui/src/App.tsx @@ -1,6 +1,5 @@ import { Page } from "@patternfly/react-core"; import { PropsWithChildren, Suspense } from "react"; -import { ErrorBoundary } from "react-error-boundary"; import { Outlet } from "react-router-dom"; import { Help, mainPageContentId } from "ui-shared"; @@ -10,6 +9,10 @@ import { AlertProvider } from "./components/alert/Alerts"; import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs"; import { ErrorRenderer } from "./components/error/ErrorRenderer"; import { KeycloakSpinner } from "./components/keycloak-spinner/KeycloakSpinner"; +import { + ErrorBoundaryFallback, + ErrorBoundaryProvider, +} from "./context/ErrorBoundary"; import { RealmsProvider } from "./context/RealmsContext"; import { RecentRealmsProvider } from "./context/RecentRealms"; import { AccessContextProvider } from "./context/access/Access"; @@ -20,21 +23,23 @@ import { SubGroups } from "./groups/SubGroupsContext"; import { AuthWall } from "./root/AuthWall"; const AppContexts = ({ children }: PropsWithChildren) => ( - - - - - - - - {children} - - - - - - - + + + + + + + + + {children} + + + + + + + + ); export const App = () => { @@ -47,13 +52,7 @@ export const App = () => { breadcrumb={} mainContainerId={mainPageContentId} > - - (window.location.href = - window.location.origin + window.location.pathname) - } - > + }> @@ -61,7 +60,7 @@ export const App = () => { - + ); diff --git a/js/apps/admin-ui/src/components/error/ErrorRenderer.tsx b/js/apps/admin-ui/src/components/error/ErrorRenderer.tsx index 3cd2d224770..57119bd16f1 100644 --- a/js/apps/admin-ui/src/components/error/ErrorRenderer.tsx +++ b/js/apps/admin-ui/src/components/error/ErrorRenderer.tsx @@ -5,12 +5,17 @@ import { AlertVariant, PageSection, } from "@patternfly/react-core"; - -import type { FallbackProps } from "react-error-boundary"; import { useTranslation } from "react-i18next"; -export const ErrorRenderer = ({ error, resetErrorBoundary }: FallbackProps) => { +import { type FallbackProps } from "../../context/ErrorBoundary"; + +export const ErrorRenderer = ({ error }: FallbackProps) => { const { t } = useTranslation(); + + const reset = () => { + window.location.href = window.location.origin + window.location.pathname; + }; + return ( { variant={AlertVariant.danger} title={error.message} actionClose={ - + } actionLinks={ - - {t("retry")} - + {t("retry")} } > diff --git a/js/apps/admin-ui/src/context/ErrorBoundary.tsx b/js/apps/admin-ui/src/context/ErrorBoundary.tsx new file mode 100644 index 00000000000..99d7cd2f510 --- /dev/null +++ b/js/apps/admin-ui/src/context/ErrorBoundary.tsx @@ -0,0 +1,76 @@ +import { + Component, + type ComponentType, + type FunctionComponent, + type GetDerivedStateFromError, + type ReactNode, +} from "react"; +import { createNamedContext, useRequiredContext } from "ui-shared"; + +export interface ErrorBoundaryContextValue { + error?: Error; + showBoundary: (error: Error) => void; +} + +const ErrorBoundaryContext = createNamedContext< + ErrorBoundaryContextValue | undefined +>("ErrorBoundaryContext", undefined); + +export const useErrorBoundary = () => useRequiredContext(ErrorBoundaryContext); + +export interface ErrorBoundaryProviderProps { + children: ReactNode; +} + +export interface ErrorBoundaryProviderState { + error?: Error; +} + +export class ErrorBoundaryProvider extends Component< + ErrorBoundaryProviderProps, + ErrorBoundaryProviderState +> { + state: ErrorBoundaryProviderState = {}; + + static getDerivedStateFromError: GetDerivedStateFromError< + ErrorBoundaryProviderProps, + ErrorBoundaryProviderState + > = (error) => { + return { error }; + }; + + showBoundary = (error: Error) => { + this.setState({ error }); + }; + + render() { + return ( + + {this.props.children} + + ); + } +} + +export interface FallbackProps { + error: Error; +} + +export interface ErrorBoundaryFallbackProps { + fallback: ComponentType; + children: ReactNode; +} + +export const ErrorBoundaryFallback: FunctionComponent< + ErrorBoundaryFallbackProps +> = ({ children, fallback: FallbackComponent }) => { + const { error } = useErrorBoundary(); + + if (error) { + return ; + } + + return children; +}; diff --git a/js/apps/admin-ui/src/utils/useFetch.ts b/js/apps/admin-ui/src/utils/useFetch.ts index a36428e778f..f1ad6b82625 100644 --- a/js/apps/admin-ui/src/utils/useFetch.ts +++ b/js/apps/admin-ui/src/utils/useFetch.ts @@ -1,5 +1,5 @@ import { DependencyList, useEffect } from "react"; -import { useErrorHandler } from "react-error-boundary"; +import { useErrorBoundary } from "../context/ErrorBoundary"; /** * Util function to only set the state when the component is still mounted. @@ -21,12 +21,11 @@ export function useFetch( callback: (param: T) => void, deps?: DependencyList, ) { - const onError = useErrorHandler(); + const { showBoundary } = useErrorBoundary(); useEffect(() => { const controller = new AbortController(); const { signal } = controller; - adminClientCall() .then((result) => { if (!signal.aborted) { @@ -35,7 +34,7 @@ export function useFetch( }) .catch((error) => { if (!signal.aborted) { - onError(error); + showBoundary(error); } }); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index f3fbb921e2f..ce1c09a72c6 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -204,9 +204,6 @@ importers: react-dropzone: specifier: ^14.2.3 version: 14.2.3(react@18.2.0) - react-error-boundary: - specifier: ^3.1.4 - version: 3.1.4(react@18.2.0) react-hook-form: specifier: ^7.49.3 version: 7.49.3(react@18.2.0) @@ -6101,16 +6098,6 @@ packages: react: 18.2.0 dev: false - /react-error-boundary@3.1.4(react@18.2.0): - resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} - engines: {node: '>=10', npm: '>=6'} - peerDependencies: - react: '>=16.13.1' - dependencies: - '@babel/runtime': 7.23.2 - react: 18.2.0 - dev: false - /react-hook-form@7.49.3(react@18.2.0): resolution: {integrity: sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==} engines: {node: '>=18', pnpm: '8'} @@ -7793,7 +7780,6 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-dropzone: 14.2.3(react@18.2.0) - react-error-boundary: 3.1.4(react@18.2.0) react-hook-form: 7.49.3(react@18.2.0) react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0) react-router-dom: 6.21.1(react-dom@18.2.0)(react@18.2.0)