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
8.15k stars 635 forks source link

HMR crash (under very specific circumstances) #1353

Open jakst opened 7 months ago

jakst commented 7 months ago

Describe the bug

I've got a real interesting HMR bug. It only happens under very specific circumstances.

This is the error message

Invariant failed: No match found for route '/_layout/a/b' while rendering the '/_layout/_layout/a/b' route. Did you mean to pass '{ strict: false }' instead?

Of course there's only one _layout file in my file tree, not two, so tanstack is adding an extra layout.

Your Example Website or App

https://stackblitz.com/edit/github-szfxdx?file=README.md

Steps to Reproduce the Bug or Issue

  1. pnpm install
  2. pnpm dev
  3. open http://localhost:3999 (you'll be redirected to http://localhost:3999/a/b, which is the problematic route)
  4. In your editor, open bQuery.graphql.ts
  5. With the browser still open, press save inside the file.
  6. HMR should trigger and render this error:
Invariant failed: No match found for route '/_layout/a/b' while rendering the '/_layout/_layout/a/b' route. Did you mean to pass '{ strict: false }' instead?

Expected behavior

HMR shouldn't crash the page when editing files.

Screenshots or Videos

No response

Platform

Stackblitz

Additional context

I can only get it to reproduce with Relay in the mix, but the double _layout in the error message still makes me believe it's tanstack causing the error.

The error only happens under the very specific circumstances I put together in the repro. If the main.tsx and router.tsx files are merged into one, the error disappears. Without lazy routes, the error disappears. Without the nested layout, the error disappears.

tannerlinsley commented 7 months ago

This is one funky bug

jakst commented 7 months ago

Additional context

  1. This is printed to the console when it happens

    [vite] hmr invalidate /app/app.tsx Could not Fast Refresh ("true" export is incompatible). Learn more at https://github.com/vitejs/vite-plugin-react-swc#consistent-components-exports
  2. Before v1.21.1 the error message was Cannot read properties of undefined (reading 'routeId'), but I suspect that's just a change in error reporting.

  3. I tested with versions as far back as the codebase would be compatible, and the lowest I could go was v1.14.6 which had the same behavior.

SeanCassiere commented 7 months ago

Just tested this using the latest version of TSR, and I don't see these error messages any more.

@jakst could you confirm?

jakst commented 7 months ago

@SeanCassiere the issue is still there in v1.26.7/v1.26.8. The error message changed a bit though. It's now Invariant failed: Could not find an active match from "/_layout/a/b"

I forgot to mention in the instructions that you have to actually make a change in the file to trigger the error, so that might be why you can't reproduce. Here's the upgraded version https://stackblitz.com/edit/github-szfxdx-jvydpf?file=__generated__%2FbQuery.graphql.ts

SeanCassiere commented 7 months ago

I forgot to mention in the instructions that you have to actually make a change in the file to trigger the error, so that might be why you can't reproduce. Here's the upgraded version https://stackblitz.com/edit/github-szfxdx-jvydpf?file=__generated__%2FbQuery.graphql.ts

Yup you are right. Honestly its hard to determine where this problem lies.

Good news is that making the project use the new experimental.enableCodeSplitting flag seems to resolve it, so hopefully when that exits the experimental stage, we should be able to close this.

jakst commented 7 months ago

I wouldn't consider it closed because of automatic code splitting though. We use Relay which indirectly enforces manual conventions on our page file names, so we need to use manual code splitting.

Until this works in all available modes, I would like to keep this open.

sethmbrown commented 6 months ago

Any updates here? Running into the same issue in our project as well.

ccapndave commented 6 months ago

My app also crashes when fast-refreshing, although it manifests as a hook provider not existing:

@react-refresh:267 Error: You used a hook from "ActorProvider" but it's not inside a <ActorProvider> component.
    at Object.useContext2 [as useActorRef] (@xstate_react.js?v=64932f4e:183:13)
    at useSystemActor (useSystemActor.ts:5:36)
    at HomePage (HomePage.tsx:17:24)
    at renderWithHooks (chunk-F6HKJT7G.js?v=43636092:12171:26)
    at updateFunctionComponent (chunk-F6HKJT7G.js?v=43636092:14577:28)
    at beginWork (chunk-F6HKJT7G.js?v=43636092:15912:22)
    at beginWork$1 (chunk-F6HKJT7G.js?v=43636092:19749:22)
    at performUnitOfWork (chunk-F6HKJT7G.js?v=43636092:19194:20)
    at workLoopSync (chunk-F6HKJT7G.js?v=43636092:19133:13)
    at renderRootSync (chunk-F6HKJT7G.js?v=43636092:19112:15

Not sure if its the same issue, but it also happens when saving a file causing a hot refresh.

ccapndave commented 6 months ago

So I managed to fix my problem and I'm not sure it is related to this issue after all but in case someone ends up here after searching HMR, here is what I found. It seems that if a context provider is declared outside of the RouterProvider, it will be lost on HMR:

<MyProvider>
  <RouterProvider router={router} />
</MyProvider>

is not ok. Instead I needed to move MyProvider inside the __root.

bolyesz commented 4 months ago

The issues still persists on the latest version, we have a /vehicles route under a _protected route, and when I save any file without changes I get this error: Invariant failed: Could not find an active match from "/_protected/vehicles/" and when I check the devtools I see a cached route match: "/_protected/_protected/vehicles/"

GutsSOLO commented 4 months ago

I also have the same issue as @bolyesz do we have any solution for this ?

GutsSOLO commented 4 months ago

@bolyesz if you use a setTimeout before your navigate({to: '/'}) it should fix the issue the time we find a solution

bolyesz commented 4 months ago

I removed all my .lazy routes and it seems that it fixed my issue, also I had problems with full page reloads on file save, which was also sorted out by these changes

TTRiznykLeo commented 4 months ago

Having similar issue, It occurs when i use useSearch and the route is under _protected

const search = useSearch({ from: '/_protected/a/b/' }) Error: Invariant failed: Could not find an active match from "/_protected/a/b/"

when i try: const search = useSearch({ from: '/a/b/' })

The issue disappears, but i getting type error: Type '"/a/b/"' is not assignable to type '"__root__" | "/" | "/_protected/a/b" | .....

using strict: false helps const search = useSearch({ strict: false })

guilhermeguidetti commented 2 months ago

same as @TTRiznykLeo, but when using useLoaderData

my layout:

export const Route = createFileRoute("/(private)/_private")({
    component: () => (
        <div>
            <MainNavBar />
            <Outlet />
        </div>
    ),
});

Profile page

export const Route = createFileRoute(
    "/(private)/_private/(profile)/perfil/$userId/"
)({
    component: UserPage,
    loader: async ({ params }) => {
        const userId = params.userId;
        const userData = await fetchUser(userId);
        return userData;
    },
    beforeLoad: async () => {
        if (!(await isAuthenticated())) {
            throw redirect({
                to: "/entrar",
            });
        }
    },
});

export default function UserPage() {
    const userData = useLoaderData({ from: "/_private/perfil/$userId/" });

    return (
        <div>
            <h1>{userData?.name}</h1>
            <p>{userData?.avatar}</p>
            {/* Renderize outras informaƧƵes do usuƔrio */}
        </div>
    );
}

Invariant failed: Could not find an active match from "/_private/perfil/$userId/"

guilhermeguidetti commented 2 months ago

same as @TTRiznykLeo, but when using useLoaderData

my layout:

export const Route = createFileRoute("/(private)/_private")({
  component: () => (
      <div>
          <MainNavBar />
          <Outlet />
      </div>
  ),
});

Profile page

export const Route = createFileRoute(
  "/(private)/_private/(profile)/perfil/$userId/"
)({
  component: UserPage,
  loader: async ({ params }) => {
      const userId = params.userId;
      const userData = await fetchUser(userId);
      return userData;
  },
  beforeLoad: async () => {
      if (!(await isAuthenticated())) {
          throw redirect({
              to: "/entrar",
          });
      }
  },
});

export default function UserPage() {
  const userData = useLoaderData({ from: "/_private/perfil/$userId/" });

  return (
      <div>
          <h1>{userData?.name}</h1>
          <p>{userData?.avatar}</p>
          {/* Renderize outras informaƧƵes do usuƔrio */}
      </div>
  );
}

Invariant failed: Could not find an active match from "/_private/perfil/$userId/"

I updated my tanstack-router package from 1.48.1 to 1.48.4 and its now fixed