diff --git a/docs/page-components.md b/docs/page-components.md index d47e295..9929212 100644 --- a/docs/page-components.md +++ b/docs/page-components.md @@ -41,6 +41,18 @@ Typically, error pages will be the same component as a Not Found or 404 page. _**Note:** If using a React component for your error page, it can receive the error message thrown by a guard function via an `error` prop._ +## Raw errors + +By default, only the `message` property of the error thrown by a guard is sent to the Error page (or if no `message` property is available, the string `"Not found."`). + +If you want the exact value thrown by your guards available on the `error` prop for your Error page you can set the `rawError` prop to `true`. + +It can be set either: + +- _globally_ as the `rawError` prop of a [`GuardProvider`](/docs/guard-provider.md) + +- _individually_ as the `rawError` prop of a [`GuardedRoute`](/docs/guarded-route.md) + ## Examples With strings: diff --git a/package/src/Guard.tsx b/package/src/Guard.tsx index e27d94c..19e7bb3 100644 --- a/package/src/Guard.tsx +++ b/package/src/Guard.tsx @@ -1,7 +1,13 @@ import React, { useCallback, useContext, useEffect, useMemo } from 'react'; import { __RouterContext as RouterContext } from 'react-router'; import { matchPath, Redirect, Route } from 'react-router-dom'; -import { ErrorPageContext, FromRouteContext, GuardContext, LoadingPageContext } from './contexts'; +import { + ErrorPageContext, + FromRouteContext, + GuardContext, + LoadingPageContext, + RawErrorContext, +} from './contexts'; import { usePrevious, useStateRef, useStateWhenMounted } from './hooks'; import renderPage from './renderPage'; import { @@ -36,6 +42,7 @@ const Guard: React.FunctionComponent = ({ children, component, meta, const guards = useContext(GuardContext); const LoadingPage = useContext(LoadingPageContext); const ErrorPage = useContext(ErrorPageContext); + const useRawErrors = useContext(RawErrorContext); const hasGuards = useMemo(() => !!(guards && guards.length > 0), [guards]); const [validationsRequested, setValidationsRequested] = useStateRef(0); @@ -122,7 +129,7 @@ const Guard: React.FunctionComponent = ({ children, component, meta, * Validates the route using the guards. If an error occurs, it * will toggle the route error state. */ - const validateRoute = async (): Promise => { + const validateRoute = async (useRawErrors: boolean | null | undefined): Promise => { const currentRequests = validationsRequested.current; let pageProps: PageProps = {}; @@ -134,7 +141,7 @@ const Guard: React.FunctionComponent = ({ children, component, meta, pageProps = props; routeRedirect = redirect; } catch (error) { - routeError = error.message || 'Not found.'; + routeError = useRawErrors ? error : error.message || 'Not found.'; } if (currentRequests === getValidationsRequested()) { @@ -146,8 +153,8 @@ const Guard: React.FunctionComponent = ({ children, component, meta, }; useEffect(() => { - validateRoute(); - }, []); + validateRoute(useRawErrors); + }, [useRawErrors]); useEffect(() => { if (hasPathChanged) { @@ -156,10 +163,10 @@ const Guard: React.FunctionComponent = ({ children, component, meta, setRouteRedirect(null); setRouteValidated(!hasGuards); if (hasGuards) { - validateRoute(); + validateRoute(useRawErrors); } } - }, [hasPathChanged]); + }, [hasPathChanged, useRawErrors]); if (hasPathChanged) { if (hasGuards) { diff --git a/package/src/GuardProvider.tsx b/package/src/GuardProvider.tsx index 7a76652..3c6ef40 100644 --- a/package/src/GuardProvider.tsx +++ b/package/src/GuardProvider.tsx @@ -1,7 +1,13 @@ import React, { useContext } from 'react'; import { __RouterContext as RouterContext } from 'react-router'; import invariant from 'tiny-invariant'; -import { ErrorPageContext, FromRouteContext, GuardContext, LoadingPageContext } from './contexts'; +import { + ErrorPageContext, + FromRouteContext, + GuardContext, + LoadingPageContext, + RawErrorContext, +} from './contexts'; import { useGlobalGuards, usePrevious } from './hooks'; import { GuardProviderProps } from './types'; @@ -11,6 +17,7 @@ const GuardProvider: React.FunctionComponent = ({ ignoreGlobal, loading, error, + rawError, }) => { const routerContext = useContext(RouterContext); invariant(!!routerContext, 'You should not use outside a '); @@ -20,12 +27,15 @@ const GuardProvider: React.FunctionComponent = ({ const loadingPage = useContext(LoadingPageContext); const errorPage = useContext(ErrorPageContext); + const useRawErrors = useContext(RawErrorContext); return ( - {children} + + {children} + diff --git a/package/src/GuardedRoute.tsx b/package/src/GuardedRoute.tsx index 2d6fcdf..da8be9b 100644 --- a/package/src/GuardedRoute.tsx +++ b/package/src/GuardedRoute.tsx @@ -3,7 +3,7 @@ import { Route } from 'react-router-dom'; import invariant from 'tiny-invariant'; import ContextWrapper from './ContextWrapper'; import Guard from './Guard'; -import { ErrorPageContext, GuardContext, LoadingPageContext } from './contexts'; +import { ErrorPageContext, GuardContext, LoadingPageContext, RawErrorContext } from './contexts'; import { useGlobalGuards } from './hooks'; import { GuardedRouteProps, PageComponent } from './types'; @@ -11,6 +11,7 @@ const GuardedRoute: React.FunctionComponent = ({ children, component, error, + rawError, guards, ignoreGlobal, loading, @@ -32,9 +33,13 @@ const GuardedRoute: React.FunctionComponent = ({ context={LoadingPageContext} value={loading}> context={ErrorPageContext} value={error}> - - {children} - + + context={RawErrorContext} + value={rawError}> + + {children} + + diff --git a/package/src/contexts.ts b/package/src/contexts.ts index 145346e..59efae0 100644 --- a/package/src/contexts.ts +++ b/package/src/contexts.ts @@ -9,3 +9,5 @@ export const FromRouteContext = createContext(null); export const GuardContext = createContext(null); export const LoadingPageContext = createContext(null); + +export const RawErrorContext = createContext(false); diff --git a/package/src/types.ts b/package/src/types.ts index 197f2e4..cab91d9 100644 --- a/package/src/types.ts +++ b/package/src/types.ts @@ -70,6 +70,7 @@ export interface BaseGuardProps { ignoreGlobal?: boolean; loading?: PageComponent; error?: PageComponent; + rawError?: boolean; } export type PropsWithMeta = T & {