How to use invalidate in hooks (auth Context)
I don't know how to use router.invalidate() on my onAuthStateChange listener to make sure that user data is always ok. The problem is useRouter needs to be called inside Router Provider and my Auth Context Provider is outside of it and it can't be changed with how tanstack router works ( I guess ).
authContext.tsx
main.tsx
import { Session, User } from "@supabase/supabase-js";
import { useContext, useState, useEffect, createContext } from "react";
import supabaseClient from "@/utils/supabase";
import { useRouter } from "@tanstack/react-router";
export interface IAuthContext {
session: Session | null;
user: User | null;
signOut: () => void;
}
// create a context for authentication
const AuthContext = createContext<{
session: Session | null | undefined;
user: User | null | undefined;
signOut: () => void;
}>({ session: null, user: null, signOut: () => {} });
export const AuthProvider = ({ children }: any) => {
const [user, setUser] = useState<User>();
const [session, setSession] = useState<Session | null>();
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
const setData = async () => {
const {
data: { session },
error,
} = await supabaseClient.auth.getSession();
if (error) throw error;
setSession(session);
setUser(session?.user);
setLoading(false);
};
const { data: listener } = supabaseClient.auth.onAuthStateChange(
(_event, session) => {
setSession(session);
setUser(session?.user);
setLoading(false);
// invalidate fails because it's not inside Router Provider
router.invalidate()
}
);
setData();
return () => {
listener?.subscription.unsubscribe();
};
}, []);
const value = {
session,
user,
signOut: () => supabaseClient.auth.signOut(),
};
// use a provider to pass down the value
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
// export the useAuth hook
export const useAuth = () => {
return useContext(AuthContext);
};
import { Session, User } from "@supabase/supabase-js";
import { useContext, useState, useEffect, createContext } from "react";
import supabaseClient from "@/utils/supabase";
import { useRouter } from "@tanstack/react-router";
export interface IAuthContext {
session: Session | null;
user: User | null;
signOut: () => void;
}
// create a context for authentication
const AuthContext = createContext<{
session: Session | null | undefined;
user: User | null | undefined;
signOut: () => void;
}>({ session: null, user: null, signOut: () => {} });
export const AuthProvider = ({ children }: any) => {
const [user, setUser] = useState<User>();
const [session, setSession] = useState<Session | null>();
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
const setData = async () => {
const {
data: { session },
error,
} = await supabaseClient.auth.getSession();
if (error) throw error;
setSession(session);
setUser(session?.user);
setLoading(false);
};
const { data: listener } = supabaseClient.auth.onAuthStateChange(
(_event, session) => {
setSession(session);
setUser(session?.user);
setLoading(false);
// invalidate fails because it's not inside Router Provider
router.invalidate()
}
);
setData();
return () => {
listener?.subscription.unsubscribe();
};
}, []);
const value = {
session,
user,
signOut: () => supabaseClient.auth.signOut(),
};
// use a provider to pass down the value
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
// export the useAuth hook
export const useAuth = () => {
return useContext(AuthContext);
};
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AuthProvider, useAuth } from "@/contexts/authContext";
import "./index.css";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// QueryClient instance
const queryClient = new QueryClient();
// Router instance
const router = createRouter({
routeTree,
defaultPreload: "intent",
context: {
queryClient: queryClient,
auth: undefined!,
},
});
// Register the router for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
function App() {
const auth = useAuth();
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<RouterProvider router={router} context={{queryClient, auth }} />
</AuthProvider>
</QueryClientProvider>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AuthProvider, useAuth } from "@/contexts/authContext";
import "./index.css";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// QueryClient instance
const queryClient = new QueryClient();
// Router instance
const router = createRouter({
routeTree,
defaultPreload: "intent",
context: {
queryClient: queryClient,
auth: undefined!,
},
});
// Register the router for type safety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
function App() {
const auth = useAuth();
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<RouterProvider router={router} context={{queryClient, auth }} />
</AuthProvider>
</QueryClientProvider>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);

1 Reply
metropolitan-bronzeOP•13mo ago
Documentation seems to use it but I don''t really know how they can use it if it's not wrapped with the Router provider