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.12k stars 630 forks source link

Search parameters get overridden on page load #2290

Closed lithdew closed 1 month ago

lithdew commented 1 month ago

Which project does this relate to?

Start

Describe the bug

export const Route = createFileRoute('/')({
  component: Home,
  validateSearch: z.object({
    step: z.enum(['a', 'b', 'c']).optional(),
  }),
  loaderDeps: ({ search: { step } }) => ({ step }),
  loader: async ({ deps: { step } }) => {
    if (step === undefined) {
      throw redirect({
        to: '/',
        search: ({ step: _, ...search }) => ({ ...search, step: 'a' }),
      })
    }
  },
})

When I navigate to /, it is expected that I get redirected to /?step=a - this works as expected.

However, when I navigate to /?step=b, I get redirected to /, and then subsequently get redirected to /?step=a.

No errors get thrown when I navigate to to /?step=b, which leads me to assume that the search parameters were properly validated by the Zod schema under validateSearch.

It is expected that when I navigate to /?step=b, that I would stay on /?step=b without being redirected to / and subsequently /?step=a.

This bug occurs irrespective of this redirect logic being placed in:

  1. beforeLoad,
  2. loader, or
  3. in component with a useEffect over Route.useSearch({ select: ({step}) => step })

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-txrzyl?file=app%2Froutes%2Findex.tsx

Steps to Reproduce the Bug or Issue

  1. Go to https://stackblitz.com/edit/tanstack-router-txrzyl?file=app%2Froutes%2Findex.tsx
  2. Press Open Preview in new tab and press Connect to project - notice that you get automatically redirected from / to /?step=a.
  3. Try navigate to /?step=b.

Expected behavior

That I stay on /?step=b as it conforms to the Zod schema provided under validateSearch.

Screenshots or Videos

No response

Platform

Additional context

No response

lithdew commented 1 month ago

Just to clarify, I tested to see if this bug can be reproduced on Tanstack Router and it cannot be reproduced. This issue only occurs with Tanstack Start.

schiller-manuel commented 1 month ago

working example for router:

https://stackblitz.com/edit/tanstack-router-f9yax9?file=src%2Froutes%2Findex.tsx&preset=node

david-arteaga commented 1 month ago

I also found this bug yesterday and experienced the search params simply being deleted completely on page load/reload. This reproduces it: https://github.com/david-arteaga/tanstack-start-search-bug

The errors described here happen both when using the dev server and when using the production build (vinxi build --preset node_server and vinxi start).

Just go to /?anyKey=anyValue. The page will load, and then the search params will get removed, and the entry in browser history will be replaced with an entry without any search params with the same path. The redirect/navigation happens on it's own.

I've also found that when using a Link component with the search prop set to a function, the prev param is undefined when reloading any page without search params. The server rendered page does work and flashes for a second, and then get's replaced with an error page saying: Cannot read properties of undefined (reading 'addBy') (for this case below). Maybe prev being undefined has something to do with why search params are removed.

import { createFileRoute, Link } from '@tanstack/react-router';
import { z } from 'zod';

export const Route = createFileRoute('/')({
  component: HomeComponent,
  validateSearch: z.object({
    addBy: z.number().optional(),
  }),
});

function HomeComponent() {
  const search = Route.useSearch();
  const { addBy = 0 } = search;
  return (
    <div className="p-2">
      <h3>Welcome Home!</h3>
      <pre>{JSON.stringify(search, null, 2)}</pre>
      <Link
        to={Route.to}
        activeOptions={{
          includeSearch: true,
        }}
        from="/"
        // the `?` is required here because prev is not always defined for some reason, which also seems to me like a bug
        search={(prev) => ({ addBy: (prev.addBy ?? 1) + 1 })}
        preload="intent"
      >
        Increase addBy ({addBy})
      </Link>
    </div>
  );
}

https://github.com/user-attachments/assets/311a822c-baf4-4697-9247-18cd815b5719


There also seems to be a Typescript bug when using the search prop on Link but that's probably a separate issue (and this happens even when routeTree.gen.ts has been generated and the dev server is running):

Type '{ addBy: number; }' is not assignable to type 'never'.ts(2322)
link.d.ts(49, 134): The expected type comes from the return type of this signature.