Repository: vercel/next.js
Pull request: Remove unused devtools code handling React without `use`

Next.js DevTools bundles their own React (Canary) which always has `use` available. We can stop handling the case where `use` doesn't exist.

Changed files:
filename: packages/next/src/next-devtools/dev-overlay/container/runtime-error/render-error.tsx
patch: @@ -68,20 +68,9 @@ const RenderRuntimeError = ({ children, state, isAppDir }: Props) => {
       return
     }
 
-    let mounted = true
-
-    getErrorByType(nextError, isAppDir).then((resolved) => {
-      if (mounted) {
-        // 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 }))
-      }
-    })
-
-    return () => {
-      mounted = false
-    }
+    const resolved = getErrorByType(nextError, isAppDir)
+    // eslint-disable-next-line react-hooks/set-state-in-effect -- TODO: fetch-while-rendering
+    setLookups((m) => ({ ...m, [resolved.id]: resolved }))
   }, [nextError, isAppDir])
 
   const totalErrorCount = errors.length

filename: packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts
patch: @@ -8,9 +8,7 @@ export type ReadyRuntimeError = {
   id: number
   runtime: true
   error: Error & { environmentName?: string }
-  frames:
-    | readonly OriginalStackFrame[]
-    | (() => Promise<readonly OriginalStackFrame[]>)
+  frames: () => Promise<readonly OriginalStackFrame[]>
   type: 'runtime' | 'console' | 'recoverable'
 }
 
@@ -19,63 +17,29 @@ export const useFrames = (
 ): readonly OriginalStackFrame[] => {
   if (!error) return []
 
-  if ('use' in React) {
-    const frames = error.frames
-
-    if (typeof frames !== 'function') {
-      throw new Error(
-        'Invariant: frames must be a function when the React version has React.use. This is a bug in Next.js.'
-      )
-    }
-
-    return React.use((frames as () => Promise<readonly OriginalStackFrame[]>)())
-  } else {
-    if (!Array.isArray(error.frames)) {
-      throw new Error(
-        'Invariant: frames must be an array when the React version does not have React.use. This is a bug in Next.js.'
-      )
-    }
-
-    return error.frames
-  }
+  const frames = error.frames
+  return React.use(frames())
 }
 
-export async function getErrorByType(
+export function getErrorByType(
   event: SupportedErrorEvent,
   isAppDir: boolean
-): Promise<ReadyRuntimeError> {
-  const baseError = {
+): ReadyRuntimeError {
+  const readyRuntimeError: ReadyRuntimeError = {
     id: event.id,
     runtime: true,
     error: event.error,
     type: event.type,
-  } as const
-
-  if ('use' in React) {
-    const readyRuntimeError: ReadyRuntimeError = {
-      ...baseError,
-      // createMemoizedPromise dedups calls to getOriginalStackFrames
-      frames: createMemoizedPromise(async () => {
-        return await getOriginalStackFrames(
-          event.frames,
-          getErrorSource(event.error),
-          isAppDir
-        )
-      }),
-    }
-    return readyRuntimeError
-  } else {
-    const readyRuntimeError: ReadyRuntimeError = {
-      ...baseError,
-      // createMemoizedPromise dedups calls to getOriginalStackFrames
-      frames: await getOriginalStackFrames(
+    // createMemoizedPromise dedups calls to getOriginalStackFrames
+    frames: createMemoizedPromise(async () => {
+      return await getOriginalStackFrames(
         event.frames,
         getErrorSource(event.error),
         isAppDir
-      ),
-    }
-    return readyRuntimeError
+      )
+    }),
   }
+  return readyRuntimeError
 }
 
 function createMemoizedPromise<T>(