TanStack / router

🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering.
https://tanstack.com/router
MIT License
7.27k stars 495 forks source link

Suspense error: A component suspended while responding to synchronous input #857

Closed schiller-manuel closed 5 months ago

schiller-manuel commented 5 months ago

Describe the bug

react-router causes the following React error:

A component suspended while responding to synchronous input.
This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.
Full stack trace as shown in console throwException@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:13798:43 handleError@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:18529:29 renderRootSync@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:18614:26 recoverFromConcurrentError@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:18233:42 performSyncWorkOnRoot@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:18375:28 flushSyncCallbacks@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:8776:30 node_modules/react-dom/cjs/react-dom.development.js/ensureRootIsScheduled/<@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/react-dom_client.js?v=ed25c456:18124:21 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 div PostsRouteComponent@https://xr7dd3-3001.csb.app/src/main.tsx?t=1702934734809:136:38 MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 RootComponent MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 Matches@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:842:18 RouterProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:1048:24 QueryClientProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-XI5YTT3J.js?v=ed25c456:2592:27 Warning: Error in route match: /posts/post CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 div PostsRouteComponent@https://xr7dd3-3001.csb.app/src/main.tsx?t=1702934734809:136:38 MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 RootComponent MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 Matches@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:842:18 RouterProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:1048:24 QueryClientProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-XI5YTT3J.js?v=ed25c456:2592:27 Warning: Error in route match: /posts/post CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 div PostsRouteComponent@https://xr7dd3-3001.csb.app/src/main.tsx?t=1702934734809:136:38 MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 Outlet2@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:938:25 RootComponent MatchInner@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:903:20 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 SafeFragment@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:865:52 Match@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:867:15 CatchBoundaryImpl@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:649:5 CatchBoundary@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:631:27 Matches@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:842:18 RouterProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-GVDQM46P.js?v=ed25c456:1048:24 QueryClientProvider@https://xr7dd3-3001.csb.app/node_modules/.vite/deps/chunk-XI5YTT3J.js?v=ed25c456:2592:27

Your Example Website or App

https://codesandbox.io/p/devbox/cool-poincare-xr7dd3?file=%2Fsrc%2Fmain.tsx%3A20%2C32

Steps to Reproduce the Bug or Issue

  1. go to https://codesandbox.io/p/devbox/cool-poincare-xr7dd3?file=%2Fsrc%2Fmain.tsx%3A20%2C32
  2. click on posts
  3. click on the first post
  4. click on the second post
  5. error is shown

Expected behavior

no error messages

Screenshots or Videos

https://github.com/TanStack/router/assets/6340397/d994f0c1-fabf-44c3-b261-a8eb2b5fb012

Platform

Additional context

No response

schiller-manuel commented 5 months ago

For the reproducer, I converted the basic-react-query example from path params to search params:

2dced4a71c448a7f59f0f6798518201ede95e9c6

OskarAsplin commented 5 months ago

I had the same issue and I think it's somehow related to the devtools. Removing the devtools solved it for me

schiller-manuel commented 5 months ago

I had the same issue and I think it's somehow related to the devtools. Removing the devtools solved it for me

issue still occurs even with devtools removed: https://github.com/TanStack/router/compare/beta...schiller-manuel:router:repro-suspense-bug

tsdevelopment commented 5 months ago

I solved it by wrapping the devtools in a Suspense component:

const TanStackRouterDevtools =
  process.env.NODE_ENV === 'production'
    ? () => null
    : lazy(() =>
        import('@tanstack/router-devtools').then((res) => ({
          default: res.TanStackRouterDevtools,
        })),
      );

const ReactQueryDevtools =
  process.env.NODE_ENV === 'production'
    ? () => null
    : lazy(() =>
        import('@tanstack/react-query-devtools').then((res) => ({
          default: res.ReactQueryDevtools,
        })),
      );

const App: FC = () => {
  return (
    <>
      <Suspense fallback={null}>
        <TanStackRouterDevtools />
        <ReactQueryDevtools />
      </Suspense>
    </>
  );
};
tannerlinsley commented 5 months ago

The solution here is to add a suspense fallback somewhere for useInfinityQuery to fall back on. TSR doesn't add any suspense wrappers by default unless you use a loader or pendingComponent explicitly.

Add this to the /posts/$postId route options and you'll be good to go:

{
  pendingComponent: () => <div>Loading...</div>,
}
rburgstaller commented 5 months ago

it would be good to update the documentation as this is not mentioned

fuad221 commented 1 month ago

I solved it by wrapping the devtools in a Suspense component:

const TanStackRouterDevtools =
  process.env.NODE_ENV === 'production'
    ? () => null
    : lazy(() =>
        import('@tanstack/router-devtools').then((res) => ({
          default: res.TanStackRouterDevtools,
        })),
      );

const ReactQueryDevtools =
  process.env.NODE_ENV === 'production'
    ? () => null
    : lazy(() =>
        import('@tanstack/react-query-devtools').then((res) => ({
          default: res.ReactQueryDevtools,
        })),
      );

const App: FC = () => {
  return (
    <>
      <Suspense fallback={null}>
        <TanStackRouterDevtools />
        <ReactQueryDevtools />
      </Suspense>
    </>
  );
};

i tried that fallback={null>

    i got this error : The value 'null' cannot be used here.ts(18050)

No quick fixes available

Expected corresponding JSX closing tag for 'div'.ts(17002) (alias) const Suspense: ExoticComponent import Suspense No quick fixes available

tonywei92 commented 1 month ago

My error: A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.

Following the instruction on new vite react typescript project. already use suspense, nothing helps

ksjitendra18 commented 1 month ago

My error: A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.

Following the instruction on new vite react typescript project. already use suspense, nothing helps

I am facing the same issue. For now I have downgraded the version to 1.17.4 and it's working fine

ravilmc commented 3 weeks ago

Just ran into this issue.

I solved it by wrapping the RouterProvider with Suspense.

import { RouterProvider, createRouter } from "@tanstack/react-router";
import { StrictMode, Suspense } from "react";
import ReactDOM from "react-dom/client";

// Import the generated route tree
import { routeTree } from "./routeTree.gen";

// Create a new router instance
const router = createRouter({ routeTree });

// Register the router instance for type safety
declare module "@tanstack/react-router" {
  interface Register {
    router: typeof router;
  }
}

// Render the app
const rootElement = document.getElementById("root")!;
if (!rootElement.innerHTML) {
  const root = ReactDOM.createRoot(rootElement);
  root.render(
    <StrictMode>
      <Suspense fallback={<div>Loading...</div>}>
        <RouterProvider router={router} />
      </Suspense>
    </StrictMode>
  );
}
MrPancakes39 commented 3 weeks ago

I just ran into this issue RouterProvider needs to be wrapped with Suspense. But why is this error occurring now? I tested this yesterday and it didn't have this issue. Weird.

StringKe commented 4 days ago

I'm having this problem in my project, I tried to create a case project but couldn't reproduce the error, does anyone know roughly where I can start to fix this?

Tracing the error reveals that the error came from a throw here.

https://github.com/TanStack/router/blob/main/packages/react-router/src/Matches.tsx#L363

CleanShot 2024-05-29 at 14 50 42@2x

video: https://share.cleanshot.com/RMvzvYmD

StringKe commented 4 days ago

The solution here is to add a suspense fallback somewhere for useInfinityQuery to fall back on. TSR doesn't add any suspense wrappers by default unless you use a loader or pendingComponent explicitly.

Add this to the /posts/$postId route options and you'll be good to go:

{
  pendingComponent: () => <div>Loading...</div>,
}

Adding pendingComponent to _root.tsx does not handle this error.

MrPancakes39 commented 3 days ago

@StringKe, I solved it like this:

<Suspense fallback={<LoadingPage />}>
    <RouterProvider router={router} />
</Suspense>
StringKe commented 3 days ago

@StringKe, I solved it like this:

<Suspense fallback={<LoadingPage />}>
    <RouterProvider router={router} />
</Suspense>

This solution solves the problem, with the negative effect that the page will have additional white screen time than before.

I'm still trying to figure out why it's causing this problem.