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

Context not getting updated in beforeload of router #1604

Closed PR4SAN closed 2 weeks ago

PR4SAN commented 2 weeks ago

Describe the bug

Followed the same example of the protected route from the documentation.

// main.tsx

import ReactDOM from 'react-dom/client'
import  {Suspense} from 'react'
import {RouterProvider, createRouter} from '@tanstack/react-router'
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
import {Amplify} from 'aws-amplify';
import awsmobile from "../aws-exports.ts";
import './index.css'
import {routeTree} from "./routeTree.gen";
import {AuthProvider, useAuth} from "@/auth.tsx";
import {Toaster} from "@/components/ui/toaster"

Amplify.configure(awsmobile);

export const queryClient = new QueryClient();

const router = createRouter({
  routeTree,
  defaultPreload: 'intent',
  context: {
    auth: undefined!,
  },
})

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

function InnerApp() {
  const auth = useAuth();
  return <RouterProvider router={router} context={{auth}}/>
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <Suspense fallback={<div>Loading...</div>}>
        <InnerApp/>
        </Suspense>
        <Toaster/>
      </AuthProvider>
    </QueryClientProvider>
  )
}

const rootElement = document.getElementById("root")!;

if (!rootElement.innerHTML) {
  const root = ReactDOM.createRoot(rootElement);
  root.render(<App />);
}
// auth.tsx

import * as React from 'react';
import {useQuery} from "@tanstack/react-query";
import {getCurrentUser} from 'aws-amplify/auth';

export interface AuthContext {
    isAuthenticated: boolean;
    user: any | null;
    isLoading: boolean;
    error: Error | null;
}

const AuthContext = React.createContext<AuthContext | null>(null)

export const useAuthQuery = () => {
    return useQuery({
        queryKey: ['auth'],
        queryFn: async () => {
            try {
                return await getCurrentUser();
            } catch (error) {
                return null;
            }
        },
        queryOptions:{
            retry: false,
            refetchOnWindowFocus: false,
        }

    });
};

export function AuthProvider({ children }: { children: React.ReactNode }) {
    const { data: user, isLoading, error } = useAuthQuery();
    const isAuthenticated = !!user
    const contextValue = { isAuthenticated, user, isLoading, error }
    console.log(contextValue,  'iniital load everything null/false but after fetch gets updated');

    return (
        <AuthContext.Provider value={{...contextValue}}>
            {children}
        </AuthContext.Provider>
    )
}

export function useAuth() {
    const context = React.useContext(AuthContext)
    if (!context) {
        throw new Error('useAuth must be used within an AuthProvider')
    }
    return context
}
// _auth.tsx

import {
    createFileRoute,
    Outlet,
    redirect,
} from '@tanstack/react-router'

export const Route = createFileRoute('/_authenticated')({
    beforeLoad: async ({ context, location }) => {
        console.log( context, 'initial load same as auth.tsx but doesn't get updated here after data loads')
        if (!context.auth.isAuthenticated) {
            throw redirect({
                to: '/login',
                search: {
                    redirect: location.href,
                },
            })
        }
    },
    component: AuthLayout,
})

function AuthLayout() {
    return (
        <div className="p-2 h-full">
            <h1>Authenticated Route</h1>
            <p>This route's content is only visible to authenticated users.</p>
            <hr />
            <Outlet />
        </div>
    )
}
// __root.tsx

import {
  createRootRouteWithContext, Outlet,
} from '@tanstack/react-router'
import {TanStackRouterDevtools} from '@tanstack/router-devtools'
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
import {AuthContext} from "@/auth.tsx";

interface RouterContext {
  auth: AuthContext
}

export const Route = createRootRouteWithContext<RouterContext>()({
  component: () => (
    <>
      <Outlet/>
      <ReactQueryDevtools buttonPosition="top-right"/>
      <TanStackRouterDevtools position="bottom-right"/>
    </>
  ),
})

Your Example Website or App

na

Steps to Reproduce the Bug or Issue

followed the same protected route example from doc but the context value for router is not getting updated

Expected behavior

context value should get updated.

Screenshots or Videos

No response

Platform

Additional context

No response

schiller-manuel commented 2 weeks ago

could you please provide this as a complete example on stackblitz (e.g. by forking one of the existing examples)?

PR4SAN commented 2 weeks ago

https://stackblitz.com/~/github.com/PR4SAN/tanstack-router

i have tried to make a version of my project removing all the details.

let me know if additional details needed.

This one from documentation where i modified some routes and some implementation to have data fetched as async and this works fine. if i am logged in and i navigate to login it redirects me to dashboard and and vice versa. https://codesandbox.io/p/devbox/elastic-bessie-thrywl?file=%2Fsrc%2Fauth.tsx%3A8%2C3-8%2C9

schiller-manuel commented 2 weeks ago

https://stackblitz.com/~/github.com/PR4SAN/tanstack-router

which steps are required to reproduce?

please also update to the latest @tanstack/react-router version to check if the issue still exists there

cpakken commented 2 weeks ago

I think we ran into the same bug, I made a repo that reproduces it: https://github.com/cpakken/issue-tanstack-router-layout-route-context

https://github.com/TanStack/router/issues/1618

PR4SAN commented 2 weeks ago

which steps are required to reproduce?

please also update to the latest @tanstack/react-router version to check if the issue still exists there

I have checked with updating the version didn't fix the issue in my project. i have updated it in stackblitz example as well.

PR4SAN commented 2 weeks ago

I think we ran into the same bug, I made a repo that reproduces it: https://github.com/cpakken/issue-tanstack-router-layout-route-context

1618

yes this is the same issue I am facing as well.

cpakken commented 2 weeks ago

@tannerlinsley just released v1.32.16 30 minutes ago and fixes #1618 issue. Please check if this issue is resolved

SeanCassiere commented 2 weeks ago

@PR4SAN The issue you are facing is because your router context is not being updated after the useQuery has finished its fetch.

You can either:

  1. call router.invalidate() after fetching
  2. or not render the <RouterProvider /> until the fetch has completed (like in image below).
image

You may also want to check out the authenticated routes example, where their concepts are put to use. Just remember, that you need to take the concepts and adapt it to work with your auth flow. Auth is not static, with every project implementing it just a little bit different from the next. https://tanstack.com/router/latest/docs/framework/react/examples/authenticated-routes

PR4SAN commented 2 weeks ago

Thanks. Invalidating the router and updating it to the latest version fixed my issue. Also, I noticed the example in the doc is updated. Thanks