diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-floating-header/error-overlay-floating-header.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-floating-header/error-overlay-floating-header.stories.tsx index 721bfd05b935c1..68fc4a8a94d512 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-floating-header/error-overlay-floating-header.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-floating-header/error-overlay-floating-header.stories.tsx @@ -20,19 +20,19 @@ export const Default: Story = { id: 0, runtime: true, error: new Error('First error message'), - frames: [], + frames: () => Promise.resolve([]), }, { id: 1, runtime: true, error: new Error('Second error message'), - frames: [], + frames: () => Promise.resolve([]), }, { id: 2, runtime: true, error: new Error('Third error message'), - frames: [], + frames: () => Promise.resolve([]), }, ], activeIdx: 1, diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-pagination/error-overlay-pagination.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-pagination/error-overlay-pagination.stories.tsx index 461f8c7b09fbc6..ecd85a925089fb 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-pagination/error-overlay-pagination.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/errors/error-overlay-pagination/error-overlay-pagination.stories.tsx @@ -20,19 +20,19 @@ const mockErrors = [ id: 1, runtime: true as const, error: new Error('First error'), - frames: [], + frames: () => Promise.resolve([]), }, { id: 2, runtime: true as const, error: new Error('Second error'), - frames: [], + frames: () => Promise.resolve([]), }, { id: 3, runtime: true as const, error: new Error('Third error'), - frames: [], + frames: () => Promise.resolve([]), }, ] diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.stories.tsx index 148151d9086a94..47c756793617d1 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.stories.tsx @@ -74,74 +74,78 @@ const readyErrors: ReadyRuntimeError[] = [ id: 1, runtime: true, error: new Error('First error message'), - frames: [ - frame, - { - ...frame, - originalStackFrame: { - ...frame.originalStackFrame, - methodName: 'ParentComponent', - lineNumber: 5, + frames: () => + Promise.resolve([ + frame, + { + ...frame, + originalStackFrame: { + ...frame.originalStackFrame, + methodName: 'ParentComponent', + lineNumber: 5, + }, }, - }, - { - ...frame, - originalStackFrame: { - ...frame.originalStackFrame, - methodName: 'GrandparentComponent', - lineNumber: 1, + { + ...frame, + originalStackFrame: { + ...frame.originalStackFrame, + methodName: 'GrandparentComponent', + lineNumber: 1, + }, }, - }, - ...Array(20).fill(ignoredFrame), - ], + ...Array(20).fill(ignoredFrame), + ]), }, { id: 2, runtime: true, error: new Error('Second error message'), - frames: [ - { - error: true, - reason: 'Second error message', - external: false, - ignored: false, - sourceStackFrame, - originalStackFrame, - originalCodeFrame: originalCodeFrame('Second error message'), - }, - ], + frames: () => + Promise.resolve([ + { + error: true, + reason: 'Second error message', + external: false, + ignored: false, + sourceStackFrame, + originalStackFrame, + originalCodeFrame: originalCodeFrame('Second error message'), + }, + ]), }, { id: 3, runtime: true, error: new Error('Third error message'), - frames: [ - { - error: true, - reason: 'Third error message', - external: false, - ignored: false, - sourceStackFrame, - originalStackFrame, - originalCodeFrame: originalCodeFrame('Third error message'), - }, - ], + frames: () => + Promise.resolve([ + { + error: true, + reason: 'Third error message', + external: false, + ignored: false, + sourceStackFrame, + originalStackFrame, + originalCodeFrame: originalCodeFrame('Third error message'), + }, + ]), }, { id: 4, runtime: true, error: new Error('Fourth error message'), - frames: [ - { - error: true, - reason: 'Fourth error message', - external: false, - ignored: false, - sourceStackFrame, - originalStackFrame, - originalCodeFrame: originalCodeFrame('Fourth error message'), - }, - ], + frames: () => + Promise.resolve([ + { + error: true, + reason: 'Fourth error message', + external: false, + ignored: false, + sourceStackFrame, + originalStackFrame, + originalCodeFrame: originalCodeFrame('Fourth error message'), + }, + ]), }, ] @@ -205,21 +209,22 @@ export const WithHydrationWarning: Story = { }, ], }), - frames: [ - { - error: true, - reason: 'First error message', - external: false, - ignored: false, - sourceStackFrame: { - file: 'app/page.tsx', - methodName: 'Home', - arguments: [], - lineNumber: 10, - column: 5, + frames: () => + Promise.resolve([ + { + error: true, + reason: 'First error message', + external: false, + ignored: false, + sourceStackFrame: { + file: 'app/page.tsx', + methodName: 'Home', + arguments: [], + lineNumber: 10, + column: 5, + }, }, - }, - ], + ]), }, ], debugInfo: { devtoolsFrontendUrl: undefined }, diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.tsx index b57125e2a49ffd..bb3f75810a0ee3 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/errors.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useEffect, useRef } from 'react' +import { useState, useMemo, useEffect, useRef, Suspense } from 'react' import type { DebugInfo } from '../../../types' import { Overlay } from '../components/overlay' import { noop as css } from '../helpers/noop-template' @@ -192,11 +192,13 @@ export function Errors({ reactOutputComponentDiff={errorDetails.reactOutputComponentDiff} /> ) : null} - + + + ) } diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/index.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/index.tsx index 78365ae924ad79..9366660a60cd54 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/index.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react' +import { use, useMemo } from 'react' import { CodeFrame } from '../../components/code-frame/code-frame' import { CallStack } from '../../components/errors/call-stack/call-stack' import { noop as css } from '../../helpers/noop-template' @@ -11,16 +11,18 @@ export type RuntimeErrorProps = { } export function RuntimeError({ error, dialogResizerRef }: RuntimeErrorProps) { + const frames = use(error.frames()) + const firstFrame = useMemo(() => { - const firstFirstPartyFrameIndex = error.frames.findIndex( + const firstFirstPartyFrameIndex = frames.findIndex( (entry) => !entry.ignored && Boolean(entry.originalCodeFrame) && Boolean(entry.originalStackFrame) ) - return error.frames[firstFirstPartyFrameIndex] ?? null - }, [error.frames]) + return frames[firstFirstPartyFrameIndex] ?? null + }, [frames]) return ( <> @@ -32,7 +34,7 @@ export function RuntimeError({ error, dialogResizerRef }: RuntimeErrorProps) { )} {error.frames.length > 0 && ( - + )} ) diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/use-error-hook.ts b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/use-error-hook.ts index 3b0fb084bfb18a..52f659946446a5 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/use-error-hook.ts +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/runtime-error/use-error-hook.ts @@ -81,25 +81,13 @@ export function useErrorHook({ if (nextError == null) { return } - let mounted = true - getErrorByType(nextError, isAppDir).then( - (resolved) => { - // We don't care if the desired error changed while we were resolving, - // thus we're not tracking it using a ref. Once the work has been done, - // we'll store it. - if (mounted) { - setLookups((m) => ({ ...m, [resolved.id]: resolved })) - } - }, - () => { - // TODO: handle this, though an edge case - } - ) + const resolved = getErrorByType(nextError, isAppDir) - return () => { - mounted = false - } + // We don't care if the desired error changed while we were resolving, + // thus we're not tracking it using a ref. Once the work has been done, + // we'll store it. + setLookups((m) => ({ ...m, [resolved.id]: resolved })) }, [nextError, isAppDir]) return { diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx index c46ac62bc2198c..3c3026072bef38 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo, useCallback } from 'react' +import { useState, useEffect, useMemo, useCallback, Suspense } from 'react' import { ACTION_UNHANDLED_ERROR, ACTION_UNHANDLED_REJECTION, @@ -162,25 +162,13 @@ export function Errors({ if (nextError == null) { return } - let mounted = true - - getErrorByType(nextError, isAppDir).then( - (resolved) => { - // We don't care if the desired error changed while we were resolving, - // thus we're not tracking it using a ref. Once the work has been done, - // we'll store it. - if (mounted) { - setLookups((m) => ({ ...m, [resolved.id]: resolved })) - } - }, - () => { - // TODO: handle this, though an edge case - } - ) - return () => { - mounted = false - } + const resolved = getErrorByType(nextError, isAppDir) + + // We don't care if the desired error changed while we were resolving, + // thus we're not tracking it using a ref. Once the work has been done, + // we'll store it. + setLookups((m) => ({ ...m, [resolved.id]: resolved })) }, [nextError, isAppDir]) const [displayState, setDisplayState] = @@ -399,9 +387,14 @@ export function Errors({ ) : undefined} - - - + + + + + diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index cb4b88b5bd1ff9..764faa04cd5a27 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -3,18 +3,21 @@ import { CodeFrame } from '../../components/CodeFrame' import type { ReadyRuntimeError } from '../../helpers/get-error-by-type' import { noop as css } from '../../helpers/noop-template' import { CallStackFrame } from './CallStackFrame' +import { use } from 'react' export type RuntimeErrorProps = { error: ReadyRuntimeError } export function RuntimeError({ error }: RuntimeErrorProps) { const [isIgnoredExpanded, setIsIgnoredExpanded] = React.useState(false) + + const frames = use(error.frames()) const { firstFrame, allLeadingFrames, trailingCallStackFrames, displayedFramesCount, } = React.useMemo(() => { - const filteredFrames = error.frames.filter((frame) => + const filteredFrames = frames.filter((frame) => isIgnoredExpanded ? true : !frame.ignored ) @@ -36,7 +39,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { firstFirstPartyFrameIndex < 0 ? 0 : firstFirstPartyFrameIndex ), } - }, [error.frames, isIgnoredExpanded]) + }, [frames, isIgnoredExpanded]) return ( @@ -64,8 +67,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { ))} { // if the default displayed ignored frames count is equal equal to the total frames count, hide the button - displayedFramesCount === error.frames.length && - !isIgnoredExpanded ? null : ( + displayedFramesCount === frames.length && !isIgnoredExpanded ? null : (