Replace react-error-boundary
with own implementation (#26094)
Closes #21515 Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
parent
0f48027ffb
commit
326ba672cd
7 changed files with 112 additions and 56 deletions
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
|
@ -34,6 +34,3 @@ updates:
|
|||
labels:
|
||||
- area/dependencies
|
||||
- team/ui
|
||||
ignore:
|
||||
- dependency-name: react-error-boundary
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,6 +23,7 @@ import { SubGroups } from "./groups/SubGroupsContext";
|
|||
import { AuthWall } from "./root/AuthWall";
|
||||
|
||||
const AppContexts = ({ children }: PropsWithChildren) => (
|
||||
<ErrorBoundaryProvider>
|
||||
<RealmsProvider>
|
||||
<RealmContextProvider>
|
||||
<WhoAmIContextProvider>
|
||||
|
@ -35,6 +39,7 @@ const AppContexts = ({ children }: PropsWithChildren) => (
|
|||
</WhoAmIContextProvider>
|
||||
</RealmContextProvider>
|
||||
</RealmsProvider>
|
||||
</ErrorBoundaryProvider>
|
||||
);
|
||||
|
||||
export const App = () => {
|
||||
|
@ -47,13 +52,7 @@ export const App = () => {
|
|||
breadcrumb={<PageBreadCrumbs />}
|
||||
mainContainerId={mainPageContentId}
|
||||
>
|
||||
<ErrorBoundary
|
||||
FallbackComponent={ErrorRenderer}
|
||||
onReset={() =>
|
||||
(window.location.href =
|
||||
window.location.origin + window.location.pathname)
|
||||
}
|
||||
>
|
||||
<ErrorBoundaryFallback fallback={ErrorRenderer}>
|
||||
<ServerInfoProvider>
|
||||
<Suspense fallback={<KeycloakSpinner />}>
|
||||
<AuthWall>
|
||||
|
@ -61,7 +60,7 @@ export const App = () => {
|
|||
</AuthWall>
|
||||
</Suspense>
|
||||
</ServerInfoProvider>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundaryFallback>
|
||||
</Page>
|
||||
</AppContexts>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<PageSection>
|
||||
<Alert
|
||||
|
@ -18,15 +23,10 @@ export const ErrorRenderer = ({ error, resetErrorBoundary }: FallbackProps) => {
|
|||
variant={AlertVariant.danger}
|
||||
title={error.message}
|
||||
actionClose={
|
||||
<AlertActionCloseButton
|
||||
title={error.message}
|
||||
onClose={resetErrorBoundary}
|
||||
/>
|
||||
<AlertActionCloseButton title={error.message} onClose={reset} />
|
||||
}
|
||||
actionLinks={
|
||||
<AlertActionLink onClick={resetErrorBoundary}>
|
||||
{t("retry")}
|
||||
</AlertActionLink>
|
||||
<AlertActionLink onClick={reset}>{t("retry")}</AlertActionLink>
|
||||
}
|
||||
></Alert>
|
||||
</PageSection>
|
||||
|
|
76
js/apps/admin-ui/src/context/ErrorBoundary.tsx
Normal file
76
js/apps/admin-ui/src/context/ErrorBoundary.tsx
Normal file
|
@ -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 (
|
||||
<ErrorBoundaryContext.Provider
|
||||
value={{ error: this.state.error, showBoundary: this.showBoundary }}
|
||||
>
|
||||
{this.props.children}
|
||||
</ErrorBoundaryContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface FallbackProps {
|
||||
error: Error;
|
||||
}
|
||||
|
||||
export interface ErrorBoundaryFallbackProps {
|
||||
fallback: ComponentType<FallbackProps>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ErrorBoundaryFallback: FunctionComponent<
|
||||
ErrorBoundaryFallbackProps
|
||||
> = ({ children, fallback: FallbackComponent }) => {
|
||||
const { error } = useErrorBoundary();
|
||||
|
||||
if (error) {
|
||||
return <FallbackComponent error={error} />;
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
|
@ -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<T>(
|
|||
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<T>(
|
|||
})
|
||||
.catch((error) => {
|
||||
if (!signal.aborted) {
|
||||
onError(error);
|
||||
showBoundary(error);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue