From b816754c065dbd78c58192d451f5e72ae3fb6a3a Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Fri, 4 Sep 2026 07:57:35 +0000
Subject: [PATCH] fix(web): use stable error boundary retry (#41811)
---
web/app/(commonLayout)/error.tsx | 10 ++---
web/app/__tests__/error.spec.tsx | 63 +++++++++++++++++++-------------
web/app/error.tsx | 32 +++++++---------
3 files changed, 57 insertions(+), 48 deletions(-)
diff --git a/web/app/(commonLayout)/error.tsx b/web/app/(commonLayout)/error.tsx
index 26400fc1fb9..9128c73cdd2 100644
--- a/web/app/(commonLayout)/error.tsx
+++ b/web/app/(commonLayout)/error.tsx
@@ -8,12 +8,12 @@ import { isLegacyBase401 } from '@/features/account-profile/client'
type Props = Readonly<{
error: Error & { digest?: string }
- unstable_retry: () => void
+ retry: () => void
}>
-export default function CommonLayoutError({ error, unstable_retry }: Props) {
+export default function CommonLayoutError({ error, retry }: Props) {
const { t } = useTranslation('common')
- const { reset } = useQueryErrorResetBoundary()
+ const { reset: resetQueries } = useQueryErrorResetBoundary()
console.error(error)
@@ -32,8 +32,8 @@ export default function CommonLayoutError({ error, unstable_retry }: Props) {
size="small"
variant="secondary"
onClick={() => {
- reset()
- unstable_retry()
+ resetQueries()
+ retry()
}}
>
{t(($) => $['errorBoundary.tryAgain'])}
diff --git a/web/app/__tests__/error.spec.tsx b/web/app/__tests__/error.spec.tsx
index c4695ca3f86..db99e79fc12 100644
--- a/web/app/__tests__/error.spec.tsx
+++ b/web/app/__tests__/error.spec.tsx
@@ -5,35 +5,48 @@ import { afterEach, describe, expect, it, vi } from 'vite-plus/test'
import CommonLayoutError from '@/app/(commonLayout)/error'
import AppError from '@/app/error'
+type ErrorRecoveryProps = Readonly<{
+ retry: () => void
+}>
+
+const routeErrors = [
+ {
+ name: 'root error',
+ renderError: (props: ErrorRecoveryProps) => ,
+ },
+ {
+ name: 'common layout error',
+ renderError: (props: ErrorRecoveryProps) => (
+
+ ),
+ },
+]
+
describe('route error recovery', () => {
afterEach(() => {
vi.restoreAllMocks()
})
- it.each([
- {
- name: 'root error',
- renderError: (retry: () => void) => ,
+ it.each(routeErrors)(
+ 'retries the $name after resetting query errors',
+ async ({ renderError }) => {
+ const user = userEvent.setup()
+ const retry = vi.fn()
+ vi.spyOn(console, 'error').mockImplementation(() => {})
+
+ render(
+
+ {({ isReset }) =>
+ renderError({
+ retry: () => retry(isReset()),
+ })
+ }
+ ,
+ )
+
+ await user.click(screen.getByRole('button', { name: 'common.errorBoundary.tryAgain' }))
+
+ expect(retry).toHaveBeenCalledWith(true)
},
- {
- name: 'common layout error',
- renderError: (retry: () => void) => (
-
- ),
- },
- ])('resets failed queries before retrying the $name', async ({ renderError }) => {
- const user = userEvent.setup()
- const retry = vi.fn()
- vi.spyOn(console, 'error').mockImplementation(() => {})
-
- render(
-
- {({ isReset }) => renderError(() => retry(isReset()))}
- ,
- )
-
- await user.click(screen.getByRole('button', { name: 'common.errorBoundary.tryAgain' }))
-
- expect(retry).toHaveBeenCalledWith(true)
- })
+ )
})
diff --git a/web/app/error.tsx b/web/app/error.tsx
index 52b9275a304..166ceee6f51 100644
--- a/web/app/error.tsx
+++ b/web/app/error.tsx
@@ -6,16 +6,14 @@ import { useTranslation } from 'react-i18next'
import { FullScreenLoading } from '@/app/components/full-screen-loading'
import { isLegacyBase401 } from '@/features/account-profile/client'
-type Props = {
+type Props = Readonly<{
error: Error & { digest?: string }
- reset?: () => void
- unstable_retry?: () => void
-}
+ retry: () => void
+}>
-export default function AppError({ error, reset, unstable_retry }: Props) {
+export default function AppError({ error, retry }: Props) {
const { t } = useTranslation('common')
const { reset: resetQueries } = useQueryErrorResetBoundary()
- const retry = reset ?? unstable_retry
console.error(error)
@@ -26,18 +24,16 @@ export default function AppError({ error, reset, unstable_retry }: Props) {
{t(($) => $['errorBoundary.message'])}
- {retry && (
-
- )}
+
)
}