Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
huozhi opened this issue Jan 12, 2023 · 17 comments
Closed

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

huozhi opened this issue Jan 12, 2023 · 17 comments

Comments

@huozhi
Copy link
Contributor

huozhi commented Jan 12, 2023

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

@RohitV5
Copy link

RohitV5 commented Jan 17, 2023

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

@saiy2k
Copy link

saiy2k commented Jan 18, 2023

Happens with v18.2.0, as well

@sundaram2021
Copy link

The problem is that the dynamic function is not providing a valid wakeable object which is expected by the markComponentSuspended function in the React DevTools.

This is a known issue that can occur when using the dynamic function with the React DevTools. One way to fix this is to disable the React DevTools while running the application, as you mentioned.

Another way to fix this issue is by not using the dynamic function and instead use the React.lazy and Suspense feature that's built in react.

One way to fix the issue with the dynamic function and the React DevTools is by providing a valid wakeable object to the markComponentSuspended function.

`import { useState, useEffect } from 'react';
import dynamic from 'dynamic-suspense-forked';

function MyComponent() {
const [Component, setComponent] = useState(null);

useEffect(() => {
let wakeable;
const load = async () => {
const module = await import('./MyLazyComponent');
setComponent(module.default);
wakeable.resolve();
};
if (typeof window !== 'undefined' && window.REACT_DEVTOOLS_GLOBAL_HOOK) {
wakeable = window.REACT_DEVTOOLS_GLOBAL_HOOK.onResolved(load);
} else {
load();
}
}, []);

if (!Component) {
return null;
}

return ;
}
`

@SukkaW
Copy link

SukkaW commented Jan 23, 2023

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).

@sundaram2021
Copy link

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.

@SukkaW
Copy link

SukkaW commented Jan 24, 2023

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).

@alainkaiser
Copy link

alainkaiser commented Jan 26, 2023

Hi guys
I am also facing this issue with the following deps:

"react": "18.2.0",
"react-dom": "18.2.0",
"next": "13.1.5",
"next-sanity": "^4.1.1",

Just tried to remove the devtools extensions from chrome and re-install it. This does not really help.
Are there any updates on this one?

The workaround provided by @SukkaW worked for me, thanks so much!

@mondaychen mondaychen self-assigned this Feb 1, 2023
@mondaychen mondaychen removed the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Feb 1, 2023
@magnusriga
Copy link

magnusriga commented Feb 26, 2023

@SukkaW Thank you for sharing your solution.

Quick question: I am using the experimental app dir, and my topmost layout is a React Server Component. Thus, I placed your function in there and wrapped it in a window check, however I think it only runs on the server (given that it is a RSC). Would I have to turn everything into RCC for your suggestion to work?

// app/layout.js

if (typeof window !== "undefined") {
    /**
     * !!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;

    // original code omitted for readability

    return reactDevToolsHookInject.apply(this, newArgs);
  };
}

@SukkaW
Copy link

SukkaW commented Feb 26, 2023

@SukkaW Thank you for sharing your solution.

Quick question: I am using the experimental app dir, and my topmost layout is a React Server Component. Thus, I placed your function in there and wrapped it in a window check, however I think it only runs on the server (given that it is a RSC). Would I have to turn everything into RCC for your suggestion to work?

@magnusriga

Use <script dangerouslySetInnerHtml={{ __html }} /> in your Next.js' root layout file, and copy the code snippet inside the dangerouslySetInnerHtml.

@magnusriga
Copy link

magnusriga commented Feb 26, 2023

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

@Cuni1017
Copy link

@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>
  );
}

@mondaychen
Copy link
Contributor

mondaychen commented Mar 1, 2023

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

@SukkaW
Copy link

SukkaW commented Mar 1, 2023

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.

@mondaychen
Copy link
Contributor

Thanks y'all!

@Solexofficial
Copy link

problem is still there. resolve by solutions @SukkaW and @Cuni1017
thanks guys!💛

@marcosriani
Copy link

Hey guys, does anyone have a solution for this? The only way to make my application work was to remove React dev tools completely

@SukkaW
Copy link

SukkaW commented Sep 3, 2023

Hey guys, does anyone have a solution for this? The only way to make my application work was to remove React dev tools completely

Please make sure you are using the latest version of React DevTools and the latest version of react@next. The workaround I provided in #25994 (comment) should not be used anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests