我用Vite+Reaction和SuperBase创建了我的应用程序的框架.
我设置了所有的路由,并创建了一个上下文来了解用户是否登录.
上下文判断本地存储是否有令牌,如果找到,它应该报告给路径‘dashboard’.
一旦登录一切正常,问题是当我刷新时,我被带回登录页面,即使令牌仍然保存在本地存储中.
我被困住了,我解决不了这个问题.有人能帮帮我吗?
我多次try 更改整个路由系统,但我不知道错误在哪里.
我希望在重新加载页面后,如果令牌存在于本地存储中,您会被重定向到‘仪表板’路径,而不是登录
App.tsx
import {
createBrowserRouter,
Navigate,
Outlet,
RouterProvider,
useLocation,
} from "react-router-dom";
import { AuthContext } from "./context/AuthContext";
import { AuthProvider } from "./context/AuthContext";
import Login from "./pages/login/login";
import { useContext } from "react";
import Dashboard from "./pages/Dashboard/dashboard";
import { Sidebar } from "./components/custom/sidebar";
const PrivateRoutes = () => {
const location = useLocation();
const { tokens } = useContext(AuthContext);
return tokens.access_token !== null ? (
<div className="bg-background h-screen">
<div className="grid lg:grid-cols-5 h-screen">
<Sidebar className="hidden lg:flex lg:flex-col h-screen" />
<div className="col-span-3 lg:col-span-4 lg:border-l">
<div className="h-full px-4 py-6 lg:px-8">
<Outlet />
</div>
</div>
</div>
</div>
) : (
<Navigate to="/login" replace state={{ from: location }} />
);
};
const router = createBrowserRouter([
{
path: "/",
element: <PrivateRoutes />,
children: [
{
path: "dashboard",
element: <Dashboard />,
},
],
},
{
path: "/login",
element: <Login />,
},
]);
function App() {
return (
<>
<AuthProvider>
<RouterProvider router={router} />
</AuthProvider>
</>
);
}
export default App;
Login.tsx
import { useContext } from "react";
import { AuthContext } from "@/context/AuthContext";
import { cn } from "@/lib/utils";
import { useSignal } from "@preact/signals-react";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { ReloadIcon } from "@radix-ui/react-icons";
import { Toaster, toast } from "sonner";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { signIn, supabase } from "@/utils/supabase";
import { useNavigate } from "react-router-dom";
const formSchema = z.object({
email: z.string().email({
message: "Email address is not valid",
}),
password: z.string().min(8, {
message: "Password must be at least 8 characters.",
}),
});
export default function Login() {
const { setTokens } = useContext(AuthContext);
const isLoading = useSignal(false);
const navigate = useNavigate();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
const onSubmit = async (data: z.infer<typeof formSchema>) => {
isLoading.value = true;
const res = await signIn(data);
if (res.error) toast.error("Errore, credenziali errate");
else {
//const { from } = location.state || { from: { pathname: "/" } };
setTokens(res.data.session.access_token, res.data.session.refresh_token);
localStorage.setItem("access_token", res.data.session.access_token);
localStorage.setItem("refresh_token", res.data.session.access_token);
supabase.auth.setSession({ access_token: res.data.session.access_token, refresh_token: res.data.session.refresh_token });
navigate('/dashboard');
}
isLoading.value = false;
};
return (
<>
<div className="container h-screen relative hidden flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
<Button asChild className={cn("absolute right-4 top-4 md:right-8 md:top-8")}>
<a href="auth">Register</a>
</Button>
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
<div className="absolute inset-0 bg-zinc-900" />
<div className="relative z-20 flex items-center text-lg font-medium">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-6 w-6"
>
<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3" />
</svg>
Acme Inc
</div>
<div className="relative z-20 mt-auto">
<blockquote className="space-y-2">
<p className="text-lg">
“This library has saved me countless hours of work and
helped me deliver stunning designs to my clients faster than
ever before.”
</p>
<footer className="text-sm">Sofia Davis</footer>
</blockquote>
</div>
</div>
<div className="lg:p-8">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[450px]">
<div className="flex flex-col space-y-2">
<h1 className="text-2xl font-semibold tracking-tight mb-2 text-center">
Login in your account
</h1>
</div>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid gap-y-3"
>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="Enter your email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Enter your password"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="pt-6 flex w-full">
<Button
className="w-full"
type="submit"
disabled={isLoading.value}
>
{isLoading.value === false ? (
<>
<span>Login</span>
</>
) : (
<>
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
please wait
</>
)}
</Button>
</div>
</form>
</Form>
</div>
</div>
</div>
<Toaster />
</>
);
}
AuthContext.tsx
import { createContext, useState, useEffect, ReactNode } from 'react';
interface Tokens {
access_token: string | null;
refresh_token: string | null;
}
interface AuthContextProps {
tokens: Tokens;
setTokens: (accessToken: string | null, refreshToken: string | null) => void;
}
const initialAuthContext: AuthContextProps = {
tokens: {
access_token: null,
refresh_token: null,
},
setTokens: () => {},
};
const AuthContext = createContext<AuthContextProps>(initialAuthContext);
interface AuthProviderProps {
children: ReactNode;
}
const AuthProvider = ({ children }: AuthProviderProps) => {
const [tokens, setTokens] = useState<Tokens>({
access_token: null,
refresh_token: null,
});
const setToken = (accessToken: string | null, refreshToken: string | null) => {
setTokens({ access_token: accessToken, refresh_token: refreshToken });
};
// Check for tokens in local storage on component mount
useEffect(() => {
const storedAccessToken = localStorage.getItem('access_token');
const storedRefreshToken = localStorage.getItem('refresh_token');
if (storedAccessToken && storedRefreshToken) {
setTokens({ access_token: storedAccessToken, refresh_token: storedRefreshToken });
}
console.log(tokens);
}, []);
return (
<AuthContext.Provider value={{ tokens, setTokens: setToken }}>
{children}
</AuthContext.Provider>
);
};
export { AuthContext, AuthProvider };