react: Bug: react devtools TypeError: wakeable.then is not a function

React version: 18.3.0-next-555ece0cd-20230112

Steps To Reproduce

  1. open https://7rerbz-3000.preview.csb.app/

Link to code example:

https://codesandbox.io/p/github/huozhi/dynamic-suspense-forked/main

The current behavior

client throw error while hydration with react devtools opening

react-dom.development.js?ac89:14291 Uncaught TypeError: wakeable.then is not a function
    at Object.markComponentSuspended (react_devtools_backend.js:5694:16)
    at markComponentSuspended (react-dom.development.js?ac89:5153:1)
    at handleThrow (react-dom.development.js?ac89:30749:1)
    at renderRootSync (react-dom.development.js?ac89:30950:1)
    at performSyncWorkOnRoot (react-dom.development.js?ac89:30465:1)
    at flushSyncCallbacks (react-dom.development.js?ac89:14277:1)
    at flushPassiveEffectsImpl (react-dom.development.js?ac89:31855:1)
    at flushPassiveEffects (react-dom.development.js?ac89:31775:1)
    at eval (react-dom.development.js?ac89:31517:1)
    at workLoop (scheduler.development.js?bcd2:275:1)
    at flushWork (scheduler.development.js?bcd2:244:1)
    at performWorkUntilDeadline (scheduler.development.js?bcd2:551:1)
    at k (preview-protocol.js:36:2929)
    at Q (preview-protocol.js:36:3113)
    at J (preview-protocol.js:36:3473)

The weakable variable from react_devtools_backend.js at this time is an error instance with message "This is not a real error. It's an implementation detail of React's selective hydration feature. If this leaks into userspace, it's a bug in React. Please file an issue.", then the later call of weakable.then(() => ...) will throw an error.

The expected behavior

Suepsne boundaries should be resolved successfully without error, but works with react devtools disabled

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 11
  • Comments: 17 (2 by maintainers)

Commits related to this issue

Most upvoted comments

I am facing the same issue here, with React & React DOM version react-dom@18.3.0-next-ee8509801-20230117 (which is supposedly the latest react@next).

Here is the workaround I used.

/**
 * !!WARNING!!
 * TEMPORARILY WORKAROUND A REACT DEVTOOLS ISSUE https://github.com/facebook/react/issues/25994
 * REMOVE AFTER THE ISSUE IS FIXED
 */
// Save the original __REACT_DEVTOOLS_GLOBAL_HOOK__.inject
const reactDevToolsHookInject = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject;
// Override the original __REACT_DEVTOOLS_GLOBAL_HOOK__.inject
// This will allow us to intercept and modify incoming injectProfilingHooks
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function inject(...args) {
  const newArgs = args.map(arg => {
    // Only modify the original arguments when injectProfilingHooks is present
    if (!arg || !arg.injectProfilingHooks) return arg;

    const { injectProfilingHooks: originalInjectProfilingHooks, ...rest } = arg;
    return {
      // Override the original injectProfilingHooks
      // This will allow us to intercept and modify incoming hooks
      injectProfilingHooks(...hooks) {
        const newHooks = hooks.map(hook => {
          // Only modify the original hooks when markComponentSuspended is present
          if (!hook || !hook.markComponentSuspended) return hook;

          // Override the original markComponentSuspended from the hook
          const { markComponentSuspended: orignalMarkComponentSuspended, ...rest2 } = hook;
          return {
            markComponentSuspended(fiber, wakeable, lanes) {
              if (typeof wakeable.then === 'function') {
                return orignalMarkComponentSuspended.call(this, fiber, wakeable, lanes);
              } else {
                // If "wakeable.then" is not a function, log a warning.
                console.warn('React DevTools issue detected and mitigated!\nSee https://github.com/facebook/react/issues/25994 for more information.', { fiber, wakeable, lanes });
              }
            },
            ...rest2
          };
        });
        originalInjectProfilingHooks.apply(this, newHooks);
      },
      ...rest
    };
  });
  return reactDevToolsHookInject.apply(this, newArgs);
};

Wrap the above code in an inline <script> and insert the script tag to your HTML, before any other script tags.

If you are using Next.js, you should insert the inline <script> tag in your _document (or in the root layout if you are experimenting Next.js appDir feature).

Can people try upgrading React to latest @next version (react@18.3.0-next-41110021f-20230301)?

@mondaychen I can no longer reproduce the issue with the latest react@next, so I guess it is fixed.

@SukkaW Thank you so much! @magnusriga Maybe this could help you, I’m also using the experimental app dir.

The \n in console.warn(...) must be removed, otherwise, It will be Uncaught SyntaxError: Invalid or unexpected token

//app/layout.tsx

import "./globals.css";
import NavBar from "./components/NavBar";
import AuthContext from "./context/AuthContext";

const __html = `
/**
 * !!WARNING!!
 * TEMPORARILY WORKAROUND A REACT DEVTOOLS ISSUE https://github.com/facebook/react/issues/25994
 * REMOVE AFTER THE ISSUE IS FIXED
 */
// Save the original __REACT_DEVTOOLS_GLOBAL_HOOK__.inject
const reactDevToolsHookInject = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject;
// Override the original __REACT_DEVTOOLS_GLOBAL_HOOK__.inject
// This will allow us to intercept and modify incoming injectProfilingHooks
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = function inject(...args) {
  const newArgs = args.map(arg => {
    // Only modify the original arguments when injectProfilingHooks is present
    if (!arg || !arg.injectProfilingHooks) return arg;

    const { injectProfilingHooks: originalInjectProfilingHooks, ...rest } = arg;
    return {
      // Override the original injectProfilingHooks
      // This will allow us to intercept and modify incoming hooks
      injectProfilingHooks(...hooks) {
        const newHooks = hooks.map(hook => {
          // Only modify the original hooks when markComponentSuspended is present
          if (!hook || !hook.markComponentSuspended) return hook;

          // Override the original markComponentSuspended from the hook
          const { markComponentSuspended: orignalMarkComponentSuspended, ...rest2 } = hook;
          return {
            markComponentSuspended(fiber, wakeable, lanes) {
              if (typeof wakeable.then === 'function') {
                return orignalMarkComponentSuspended.call(this, fiber, wakeable, lanes);
              } else {
                // If "wakeable.then" is not a function, log a warning.
                console.warn('React DevTools issue detected and mitigated! See https://github.com/facebook/react/issues/25994 for more information.', { fiber, wakeable, lanes });
              }
            },
            ...rest2
          };
        });
        originalInjectProfilingHooks.apply(this, newHooks);
      },
      ...rest
    };
  });
  return reactDevToolsHookInject.apply(this, newArgs);
};
`;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      {/*
        <head /> will contain the components returned by the nearest parent
        head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
      */}
      <head />
      <body>
        <main className="bg-gray-100 min-h-screen w-screen">
          <main className="max-w-screen-2xl m-auto bg-white">
            <AuthContext>
              <NavBar />
              {children}
            </AuthContext>
          </main>
        </main>
        <script dangerouslySetInnerHTML={{ __html }} />
      </body>
    </html>
  );
}

Facing same issue when using suspense with Sanity CMS. Disabling the react dev tool makes the error go away.

ut file, and copy the code snippet inside the dangerouslySetInnerHtml.

@SukkaW Sorry, I am being slow. What part of the code do I copy in where? If I replace { __html }, it needs to be with a JS expression, so I cannot use your full statement list.

Putting the whole code into an IIFE gives me this excaption:

image

please check and make sure React DevTools are properly installed and that the versions of React and React-DOM match. Also, ensure that the app is running in development mode.