diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/enqueue-client-error.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/enqueue-client-error.ts new file mode 100644 index 0000000000000..c70f6fb9ccad7 --- /dev/null +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/enqueue-client-error.ts @@ -0,0 +1,22 @@ +import { isHydrationError } from '../../../is-hydration-error' + +// Dedupe the two consecutive errors: If the previous one is same as current one, ignore the current one. +export function enqueueConsecutiveDedupedError( + queue: Array, + error: Error +) { + const isFront = isHydrationError(error) + const previousError = isFront ? queue[0] : queue[queue.length - 1] + // Only check message to see if it's the same error, as message is representative display in the console. + if (previousError && previousError.message === error.message) { + return + } + // TODO: change all to push error into errorQueue, + // currently there's a async api error is always erroring while hydration error showing up. + // Move hydration error to the front of the queue to unblock. + if (isFront) { + queue.unshift(error) + } else { + queue.push(error) + } +} diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts index 31352c90f002d..13ffcfbcaa099 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts @@ -1,11 +1,11 @@ import { useEffect } from 'react' -import { isHydrationError } from '../../../is-hydration-error' import { attachHydrationErrorState } from './attach-hydration-error-state' import { isNextRouterError } from '../../../is-next-router-error' import { storeHydrationErrorStateFromConsoleArgs } from './hydration-error-info' import { formatConsoleArgs } from '../../../../lib/console' import isError from '../../../../../lib/is-error' import { ConsoleError } from './console-error' +import { enqueueConsecutiveDedupedError } from './enqueue-client-error' export type ErrorHandler = (error: Error) => void @@ -30,14 +30,7 @@ export function handleClientError( storeHydrationErrorStateFromConsoleArgs(...consoleErrorArgs) attachHydrationErrorState(error) - // TODO: change all to push error into errorQueue, - // currently there's a async api error is always erroring while hydration error showing up. - // Move hydration error to the front of the queue to unblock. - if (isHydrationError(error)) { - errorQueue.unshift(error) - } else { - errorQueue.push(error) - } + enqueueConsecutiveDedupedError(errorQueue, error) for (const handler of errorHandlers) { handler(error) } diff --git a/test/development/app-dir/capture-console-error/app/browser/page.js b/test/development/app-dir/capture-console-error/app/browser/event/page.js similarity index 100% rename from test/development/app-dir/capture-console-error/app/browser/page.js rename to test/development/app-dir/capture-console-error/app/browser/event/page.js diff --git a/test/development/app-dir/capture-console-error/app/browser/render/page.js b/test/development/app-dir/capture-console-error/app/browser/render/page.js new file mode 100644 index 0000000000000..79fe51b77f328 --- /dev/null +++ b/test/development/app-dir/capture-console-error/app/browser/render/page.js @@ -0,0 +1,6 @@ +'use client' + +export default function Page() { + console.error('trigger an console.error in render') + return

render

+} diff --git a/test/development/app-dir/capture-console-error/capture-console-error.test.ts b/test/development/app-dir/capture-console-error/capture-console-error.test.ts index 4579d8a25488b..ac634e18ba287 100644 --- a/test/development/app-dir/capture-console-error/capture-console-error.test.ts +++ b/test/development/app-dir/capture-console-error/capture-console-error.test.ts @@ -28,7 +28,7 @@ describe('app-dir - capture-console-error', () => { }) it('should capture browser console error and format the error message', async () => { - const browser = await next.browser('/browser') + const browser = await next.browser('/browser/event') await browser.elementByCss('button').click() await waitForAndOpenRuntimeError(browser) @@ -42,7 +42,7 @@ describe('app-dir - capture-console-error', () => { "callStacks": "", "count": 1, "description": "trigger an console ", - "source": "app/browser/page.js (7:17) @ onClick + "source": "app/browser/event/page.js (7:17) @ onClick 5 |