router icon indicating copy to clipboard operation
router copied to clipboard

Infinite redirects when throwing a redirect with `to: '.'`

Open melv-n opened this issue 1 year ago • 2 comments

Which project does this relate to?

Router

Describe the bug

We used to be able to throw a redirect to update the route params, like so:

// This was both type-safe and working before a few versions ago, now it's still working but _not_ type-safe:
throw redirect({ 
  from: Route.fullPath,
  params: (params) => ({ ...params, id: newId }),
})

This actually still works at runtime but no longer type checks since a few TSR versions back, we're expected to pass the to: '.' to get loose types:

// TS is happy with this, but you'll be in a world of hurt trying to load this page... 🥶
throw redirect({ 
  from: Route.fullPath,
  to: '.',
  params: (params) => ({ ...params, id: newId }),
})

However, throwing a redirect with a to of '.' and updating just a route param causes an infinite redirect loop, causing the browser tab to freeze to a halt.

Known workarounds for this is:

  • Omitting to to get the proper runtime behaviour and using // @ts-expect-error to silence the typescript errors you'll get when trying to set params, search, etc.; OR
  • Providing a full path to to - this is however not always possible when the redirect is thrown in a generic guard/helper function where the current fullPath is not available
// This works, but we won't always have the `fullPath` available, 
// such as in re-used guard/helper functions. For this I expect the `to: '.'` to work
throw redirect({ 
  from: Route.fullPath,
  to: Route.fullPath,
  params: (params) => ({ ...params, id: newId }),
})

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-uyaeym?file=src%2Froutes%2Fusers.%24id.tsx

Steps to Reproduce the Bug or Issue

  1. Go to the page
  2. Press [Go to Logged in User]
  3. Your browser tab is now stuck in an infinite redirect loop

Expected behavior

I expect to be able to throw a redirect to only update the route params. According to the docs we can make the type checks happy by passing to: '.', but this affects the runtime behaviour.

Screenshots or Videos

No response

Platform

macOS, Arc 1.61

Additional context

No response

melv-n avatar Sep 26 '24 12:09 melv-n

the infinite loop is definitely a bug, we'll look into it. however, based on your descriptiont I would expect that you are only throwing such a redirect using your helper function if the route /users/$id is matched. So I would set from: '/users/$id' in that helper.

schiller-manuel avatar Sep 26 '24 16:09 schiller-manuel

the infinite loop is definitely a bug, we'll look into it. however, based on your descriptiont I would expect that you are only throwing such a redirect using your helper function if the route /users/$id is matched. So I would set from: '/users/$id' in that helper.

You are right that my example is a bit skewed, I wanted to keep the repro as simple as possible. In the blitzstack you can easily omit the to: '.' and it will work as expected, but that doesn't carry over very well when moving the throw redirect to a generic guard/helper where the from isn't necessarily known, or at best a string type.

melv-n avatar Sep 26 '24 18:09 melv-n

can you please check if this issue still exists? a related issue was fixed recently.

schiller-manuel avatar Feb 15 '25 01:02 schiller-manuel

throw redirect({
        to: '.',
        params: (params) => ({
          ...params,
          id: 'current-user-id',
        }),

works now

schiller-manuel avatar Mar 06 '25 00:03 schiller-manuel