Skip to content

Commit

Permalink
refactor: Error boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
garethfuller committed Sep 2, 2024
1 parent e6576d1 commit d3ca560
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 209 deletions.
55 changes: 1 addition & 54 deletions app/(app)/debug/alerts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { BalAlertButton } from '@/lib/shared/components/alerts/BalAlertButton'
import { BalAlertContent } from '@/lib/shared/components/alerts/BalAlertContent'
import { useGlobalAlerts } from '@/lib/shared/components/alerts/GlobalAlertsProvider'
import { GenericError } from '@/lib/shared/components/errors/GenericError'
import { Button, Text, VStack } from '@chakra-ui/react'
import { ErrorBoundary } from '@/lib/shared/components/errors/ErrorBoundary'
import { useEffect, useState } from 'react'
import { Button, VStack } from '@chakra-ui/react'

const exceptionName = 'Error fetching swap'
const exceptionMessage = `Execution reverted for an unknown reason. Raw Call Arguments:
Expand Down Expand Up @@ -57,7 +55,6 @@ export default function Page() {
>
Show global warning alert
</Button>
<DebugErrorBoundary />
</VStack>
)
}
Expand All @@ -73,53 +70,3 @@ function TitleWithButton({ title }: { title: string }) {
</BalAlertContent>
)
}

function DebugErrorBoundary() {
const [error, setError] = useState<Error | undefined>()

function generateDelayedError() {
setTimeout(() => {
setError(new TestError(exceptionName, exceptionMessage))
}, 3000)
}

function onReset() {
setError(undefined)
generateDelayedError()
}

useEffect(() => {
generateDelayedError()
}, [])

return (
<VStack width="full">
<Text fontWeight="bold">Default Error Boundary</Text>
<ErrorBoundary onReset={onReset}>
<Throwable error={error} />
</ErrorBoundary>
<Text fontWeight="bold">Resetable Error Boundary</Text>
<ErrorBoundary
onReset={onReset}
fallbackRender={({ resetErrorBoundary }) => (
<Button color="font.warning" onClick={resetErrorBoundary}>
Try Reset Error
</Button>
)}
>
<Throwable error={error} />
</ErrorBoundary>
<Text fontWeight="bold">Custom Error Boundary</Text>
<ErrorBoundary fallback={<Text color="font.warning">Custom Error Content</Text>}>
<Throwable error={error} />
</ErrorBoundary>
</VStack>
)
}

function Throwable({ error }: { error?: Error }) {
if (error) {
throw error
}
return <Text>Waiting for error...</Text>
}
20 changes: 3 additions & 17 deletions app/(app)/pools/[chain]/[variant]/[id]/error.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
'use client'
import { default as NextLink } from 'next/link'
import PageError from '@/lib/shared/components/errors/PageError'
import { Button, Link } from '@chakra-ui/react'

export default function Error({ error }: { error: Error }) {
return (
<PageError
title={'Something went wrong!'}
error={error}
captureException
customButton={
<Link as={NextLink} href={'/pools'} prefetch={true}>
<Button variant="outline">Reload Page</Button>
</Link>
}
/>
)
}
import { PageErrorBoundary } from '@/lib/shared/components/errors/ErrorBoundary'

export default PageErrorBoundary
24 changes: 2 additions & 22 deletions app/error.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
'use client'

import PageError from '@/lib/shared/components/errors/PageError'
import { PageErrorBoundary } from '@/lib/shared/components/errors/ErrorBoundary'

/**
* Global App ErrorBoundary.
*
* Catches:
* - components errors (e.g. throw new Error() inside render function)
*
* @see https://nextjs.org/docs/app/building-your-application/routing/error-handling#using-error-boundaries
*/
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
const title = error.digest
? `Something went wrong (Digest - ${error.digest})`
: 'Something went wrong'

return <PageError error={error} onReset={reset} title={title} captureException />
}
export default PageErrorBoundary
4 changes: 1 addition & 3 deletions app/global-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export default function GlobalError({
Sentry.captureException(error)
}, [error])

const title = error.digest
? `Something went wrong (Digest - ${error.digest})`
: 'Something went wrong'
const title = error.digest ? `Something went wrong (${error.digest})` : 'Something went wrong'

return (
<html>
Expand Down
6 changes: 5 additions & 1 deletion lib/modules/pool/PoolList/PoolListLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { FilterTags, PoolListFilters, useFilterTagsVisible } from './PoolListFil
import { PoolListTable } from './PoolListTable/PoolListTable'
import { usePoolList } from './PoolListProvider'
import { fNum } from '@/lib/shared/utils/numbers'
import { ErrorBoundary } from 'react-error-boundary'
import { BoundaryError } from '@/lib/shared/components/errors/ErrorBoundary'

export function PoolListLayout() {
const { pools, loading, count } = usePoolList()
Expand Down Expand Up @@ -65,7 +67,9 @@ export function PoolListLayout() {
<PoolListFilters />
</Stack>
</Stack>
<PoolListTable pools={pools} count={count || 0} loading={loading} />
<ErrorBoundary FallbackComponent={BoundaryError}>
<PoolListTable pools={pools} count={count || 0} loading={loading} />
</ErrorBoundary>
</VStack>
)
}
95 changes: 31 additions & 64 deletions lib/shared/components/errors/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,44 @@
'use client'

import {
ErrorBoundary as BaseErrorBoundary,
ErrorBoundaryProps as BaseErrorBoundaryProps,
FallbackProps as BaseFallbackProps,
} from 'react-error-boundary'
import { GenericError } from '@/lib/shared/components/errors/GenericError'
import { Button, Link, Stack } from '@chakra-ui/react'
import NextLink from 'next/link'
import { isDev, isStaging } from '@/lib/config/app.config'
import { useLocationFullPath } from '@/lib/shared/hooks/useLocationFullPath'
import { PropsWithChildren, ReactNode } from 'react'
import { FallbackProps } from 'react-error-boundary'
import { Button, Box, Text, Heading, VStack } from '@chakra-ui/react'
import { ensureError } from '../../utils/errors'
import { DefaultPageContainer } from '../containers/DefaultPageContainer'

export interface ErrorBoundaryProps extends PropsWithChildren {
fallback?: BaseErrorBoundaryProps['fallback']
fallbackRender?: BaseErrorBoundaryProps['fallbackRender']
onReset?: BaseFallbackProps['resetErrorBoundary']
}

export interface FallbackProps {
error: BaseFallbackProps['error']
resetErrorBoundary?: BaseFallbackProps['resetErrorBoundary']
title?: string
showReloadButton?: boolean
customButton?: ReactNode
}

export function DefaultFallbackRender({
export function BoundaryError({
error,
resetErrorBoundary,
title,
customButton,
showReloadButton = true,
}: FallbackProps) {
const showResetButton = isDev || isStaging

const path = useLocationFullPath()

const showButtons = customButton || showReloadButton || showResetButton
}: {
error: Error & { digest?: string }
resetErrorBoundary: () => void
}) {
const _error = ensureError(error)

return (
<div role="alert">
<GenericError error={error} customErrorName={title} />
{showButtons && (
<Stack direction="row" mt="4" spacing="4">
{customButton}
{showReloadButton && (
<Link as={NextLink} href={path}>
<Button variant="outline">Reload Page</Button>
</Link>
)}
{showResetButton && resetErrorBoundary && (
<Button variant="outline" onClick={() => resetErrorBoundary()}>
Try Reset (dev)
</Button>
)}
</Stack>
)}
</div>
<Box w="full" minH="200px" border="2px dashed" borderColor="red.500" p="md" rounded="lg">
<VStack align="start" spacing="md">
<Heading size="md">Something went wrong! :(</Heading>
<VStack align="start" spacing="xs">
<Text>
{_error?.name
? `${_error?.name}: ${_error?.shortMessage || ''}`
: _error?.shortMessage || ''}
</Text>
<Text>{_error?.message}</Text>
</VStack>

<Button size="sm" onClick={resetErrorBoundary}>
Try again
</Button>
</VStack>
</Box>
)
}

export function ErrorBoundary({ children, fallback, onReset, fallbackRender }: ErrorBoundaryProps) {
if (fallback) {
return <BaseErrorBoundary fallback={fallback}>{children}</BaseErrorBoundary>
}

export function PageErrorBoundary(props: FallbackProps) {
return (
<BaseErrorBoundary
onReset={onReset}
FallbackComponent={fallbackRender ?? DefaultFallbackRender}
>
{children}
</BaseErrorBoundary>
<DefaultPageContainer>
<BoundaryError {...props} />
</DefaultPageContainer>
)
}
48 changes: 0 additions & 48 deletions lib/shared/components/errors/PageError.tsx

This file was deleted.

0 comments on commit d3ca560

Please sign in to comment.