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.26k stars 657 forks source link

Infinite loop when changing search params in beforeload #2767

Open enyelsequeira opened 1 week ago

enyelsequeira commented 1 week ago

Which project does this relate to?

Router

Describe the bug

The issue is that beforeLoad should be able to change search params, however that is not the case. Based on a condition it will go into a loop always redirecting the user

Your Example Website or App

https://stackblitz.com/edit/tanstack-router-klcjz7?file=src%2Froutes%2Fposts.tsx

Steps to Reproduce the Bug or Issue

  1. In the post page you can see the mock logic

Expected behavior

should not loop the user and should put the search param in the url

Screenshots or Videos

No response

Platform

MacOS running on stackblitz

Additional context

hey @schiller-manuel here is the issue from yesterday, to help keep track of it

jiwlee97 commented 2 days ago

The issue arises because search.isFirstLogin is inconsistently handled as either a boolean or a string.

Solution 1: Treat search.isFirstLogin as a boolean:

  validateSearch: (search?:  { isFirstLogin: boolean }) => {
    return {
      isFirstLogin: search?.isFirstLogin === true,
    };
  },
  beforeLoad: async ({ context, search }) => {
    const data = await context.queryClient.fetchQuery({
      ...getMeOptions({
        id: 1,
      }),
    });

    if (
      data.firstLogin &&
      data?.type === 'USER' &&
      search.isFirstLogin !== true
    ) {
      console.log('DEMO HGERE');
      throw redirect({
        to: '/posts',
        search: {
          isFirstLogin: true,
        },
      });
    }
  },

Solution 2: Alternatively, treat search.isFirstLogin as a string:

  validateSearch: (search?: { isFirstLogin: 'true' | 'false' }) => {
    return {
      isFirstLogin: `${search?.isFirstLogin === 'true'}`,
    };
  },
  beforeLoad: async ({ context, search }) => {
    const data = await context.queryClient.fetchQuery({
      ...getMeOptions({
        id: 1,
      }),
    });

    if (
      data.firstLogin &&
      data?.type === 'USER' &&
      search.isFirstLogin !== 'true'
    ) {
      console.log('DEMO HGERE');
      throw redirect({
        to: '/posts',
        search: {
          isFirstLogin: 'true',
        },
      });
    }
  },