如何在TanStack Router中实现类似React的上下文管理?
在TanStack Router中实现Auth上下文管理
以下是基于你现有React AuthContext代码,在TanStack Router中实现认证状态管理的两种方案,兼顾兼容性和TanStack Router的特性:
方案一:保留原有React Context,集成到TanStack Router
TanStack Router完全兼容React Context,你可以保留现有代码,只需要调整集成方式,同时利用TanStack Router的路由保护能力。
1. 保留原有Auth代码
你的AuthContext.tsx和AuthContextProvider.tsx无需修改,继续作为状态管理的核心。
2. 将AuthProvider包裹在Router外层
在路由入口文件(如main.tsx或App.tsx)中,把Auth上下文提供者包裹在TanStack Router外面:
import { RouterProvider, createRouter } from '@tanstack/react-router'; import AuthContextProvider from './AuthContextProvider'; import { routes } from './routes'; const router = createRouter({ routes }); function App() { return ( <AuthContextProvider> <RouterProvider router={router} /> </AuthContextProvider> ); } export default App;
3. 在路由组件中使用Auth状态
和之前一样,在任何路由组件里用useAuthContext()获取认证信息:
import { useAuthContext } from './AuthContext'; function ProfilePage() { const { user, isAuthenticated } = useAuthContext(); if (!isAuthenticated) { return <div>请先登录</div>; } return <div>欢迎回来,{user?.name}</div>; }
4. 实现路由级认证保护
TanStack Router的beforeLoad钩子可以在路由加载前拦截未认证请求,要让钩子能访问Auth状态,需要把Auth状态同步到Router Context:
修改AuthContextProvider同步状态到Router Context
import {ReactNode, useEffect, useState} from 'react'; import {AuthContext} from './AuthContext'; import {Token, User} from '@/types'; import {QUERY_KEYS} from '@/lib/react-query/queryKeys'; import {jwtDecode} from 'jwt-decode'; import { useRouter } from '@tanstack/react-router'; const AuthContextProvider = ({children}: {children: ReactNode}) => { const [user, setUser] = useState<User | null>(null); const [token, setToken] = useState<Token | null>(null); const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); const router = useRouter(); useEffect(() => { const storedToken = JSON.parse(localStorage.getItem(QUERY_KEYS.TOKEN) || 'null'); if (storedToken) { const decodedToken = jwtDecode(storedToken.accessToken) as User; if (decodedToken.exp && decodedToken.exp * 1000 < Date.now()) { localStorage.removeItem(QUERY_KEYS.TOKEN); setToken(null); setUser(null); setIsAuthenticated(false); } else { setToken(storedToken); setUser(decodedToken); setIsAuthenticated(true); } } }, []); // 同步Auth状态到Router Context,让loader/beforeLoad能访问 useEffect(() => { router.setContext(old => ({ ...old, auth: { user, token, isAuthenticated, setUser, setToken, setIsAuthenticated } })); }, [user, token, isAuthenticated, setUser, setToken, setIsAuthenticated, router]); const contextValue = { user, setUser, token, setToken, isAuthenticated, setIsAuthenticated, }; return ( <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider> ); }; export default AuthContextProvider;
初始化Router Context
const router = createRouter({ routes, context: { auth: { user: null, token: null, isAuthenticated: false, setUser: () => {}, setToken: () => {}, setIsAuthenticated: () => {}, }, }, });
在路由中添加保护
import { createFileRoute } from '@tanstack/react-router'; export const Route = createFileRoute('/profile')({ beforeLoad: async ({ redirect, context }) => { if (!context.auth.isAuthenticated) { await redirect({ to: '/login' }); } }, component: ProfilePage, });
方案二:完全基于TanStack Router Context实现
如果不想依赖React Context,可以直接用TanStack Router自带的Context管理认证状态:
1. 编写Auth状态工具函数
创建auth.ts:
import { Token, User } from '@/types'; import { QUERY_KEYS } from '@/lib/react-query/queryKeys'; import { jwtDecode } from 'jwt-decode'; // 初始化认证状态 export const initializeAuth = () => { const storedToken = JSON.parse(localStorage.getItem(QUERY_KEYS.TOKEN) || 'null'); if (!storedToken) { return { user: null, token: null, isAuthenticated: false }; } const decodedToken = jwtDecode(storedToken.accessToken) as User; if (decodedToken.exp && decodedToken.exp * 1000 < Date.now()) { localStorage.removeItem(QUERY_KEYS.TOKEN); return { user: null, token: null, isAuthenticated: false }; } return { user: decodedToken, token: storedToken, isAuthenticated: true }; }; // 更新认证状态并同步到localStorage export const updateAuth = (router: any, newState: Partial<{ user: User | null; token: Token | null; isAuthenticated: boolean }>) => { router.setContext(old => ({ ...old, auth: { ...old.auth, ...newState }, })); if (newState.token) { localStorage.setItem(QUERY_KEYS.TOKEN, JSON.stringify(newState.token)); } else { localStorage.removeItem(QUERY_KEYS.TOKEN); } };
2. 初始化Router Context
import { RouterProvider, createRouter } from '@tanstack/react-router'; import { initializeAuth, updateAuth } from './auth'; import { routes } from './routes'; const router = createRouter({ routes, context: { auth: { ...initializeAuth(), setUser: (user: User | null) => updateAuth(router, { user }), setToken: (token: Token | null) => updateAuth(router, { token }), setIsAuthenticated: (isAuthenticated: boolean) => updateAuth(router, { isAuthenticated }), }, }, });
3. 在组件中使用Auth状态
import { useRouterContext } from '@tanstack/react-router'; function ProfilePage() { const { auth } = useRouterContext(); if (!auth.isAuthenticated) { return <div>请先登录</div>; } return <div>欢迎回来,{auth.user?.name}</div>; }
4. 在Loader/Action中访问Auth状态
TanStack Router的loader和action可以直接通过context获取认证信息:
export const Route = createFileRoute('/api/profile')({ loader: async ({ context }) => { if (!context.auth.isAuthenticated) { throw new Response('Unauthorized', { status: 401 }); } const profile = await fetch(`/api/user/${context.auth.user?.id}`); return profile.json(); }, });
关键注意事项
- TanStack Router的
beforeLoad、loader、action运行在组件外部,无法使用React Hooks,必须通过Router Context获取认证状态。 - 若混用两种方案,需确保React Context和Router Context的状态同步,避免数据不一致。
- 除了初始化时检查token过期,还需在API返回401时触发状态重置和登录跳转。
内容的提问来源于stack exchange,提问作者Aniket Tiwari
相关产品推荐
相关产品推荐

