我正在尝试使用 JWT 私有化一些路由,当登录数据保存在 AuthProvider 的状态时一切都很好,因为它与令牌相对应,但是当我重新加载页面时,数据保存在“子字段”中状态和用户令牌以某种方式丢失。这给我带来了问题,因为当页面重新加载时,它会再次重定向到登录(公共路径),只要用户未登录,这就是正确的。
验证提供者
import { useState, useEffect, createContext } from "react";
import clientAxios from "../config/ClientAxios";
import { useNavigate } from "react-router-dom";
const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
const navigate = useNavigate();
useEffect(() => {
const authUser = async () => {
const token = localStorage.getItem("token");
if (!token) {
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
};
try {
const data = await clientAxios("/usuarios/perfil", config);
setAuth(data);
navigate('/inicio')
} catch (error) {
setAuth({});
}
};
authUser();
}, []);
return (
<AuthContext.Provider
value={{
auth,
setAuth,
}}
>
{children}
</AuthContext.Provider>
);
};
export { AuthProvider };
export default AuthContext;
受保护的路线
import React from "react";
import { Outlet, Navigate } from "react-router-dom";
import useAuth from "../hooks/useAuth";
import Header from "../components/Header";
const ProtectedRoute = () => {
const { auth } = useAuth();
return(
<div>
{auth._id ? (
<div className="bg-gray-100">
<Header/>
<div className="md:min-h-screen">
<main>
<Outlet/>
</main>
</div>
</div>
)
:<Navigate to="/"/> }
</div>
)
};
export default ProtectedRoute;
使用验证
import { useContext } from "react";
import AuthContext from "../context/AuthProvider";
const useAuth = () => {
return useContext(AuthContext);
};
export default useAuth;
登录
import { Link, useNavigate } from "react-router-dom";
import { useState } from "react";
import Alert from "../components/Alert";
import clientAxios from "../config/ClientAxios";
import useAuth from "../hooks/useAuth";
import { AuthProvider } from "../context/AuthProvider";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [alert, setAlert] = useState({});
const {setAuth}=useAuth()
const navigate=useNavigate()
const handleSubmit = async (e) => {
e.preventDefault();
if ([email, password].includes("")) {
setAlert({
msg: "Hay uno o mas campos vacios",
error: true,
});
return;
}
try {
const {data}=await clientAxios.post('/usuarios/',{email,password})
setAlert({})
localStorage.setItem('token',data.token)
setAuth(data)
navigate('/inicio')
} catch (error) {
setAlert({
msg:error.response.data.msg,
error:true
})
}
};
const { msg } = alert;
return (
<div>
<h1 className="text-sky-600 font-black text-4xl capitalize mt-3">
Bienvenido de vuelta !
</h1>
<h3 className="text-gray-400 text-xl font-black">
{" "}
Inicia sesion y mejora tu
<span className="text-sky-500"> ambiente laboral</span>
</h3>
{msg && <Alert alert={alert} />}
<form onSubmit={handleSubmit} className="my-5">
<label
htmlFor="email"
className=" font-bold uppercase block text-xl text-gray-600"
>
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email Laboral"
className="w-full mt-3 p-3 border rounded-xl bg-gray-200 cursor-pointer"
/>
<label
htmlFor="password"
className=" font-bold uppercase block text-xl text-gray-600 mt-3"
>
Contraseña
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Contraseña"
className="w-full mt-3 p-3 border rounded-xl bg-gray-200 cursor-pointer"
/>
<input
type="submit"
value="Iniciar Sesion"
className="bg-sky-700 text-white w-full py-3 mt-5 font-bold rounded-md uppercase hover:cursor-pointer hover:bg-sky-900 transition-colors"
/>
</form>
<nav>
<Link
className="hover:underline block text-center my-5 text-slate-500 font-semibold uppercase text-sm"
to="forget-password"
>
Cambia tu Contraseña
</Link>
</nav>
</div>
);
};
export default Login;
正常流程应该是,当页面重新加载时,用户的数据和令牌都不会丢失,因为这是在其他私有路由中执行其他操作所必需的。
您的受保护路由需要知道您是否仍在加载身份验证,您可以尝试类似的操作
const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
const [isLoading, setIsLoading] = useState(true); // start of with loading as true
const navigate = useNavigate();
useEffect(() => {
const authUser = async () => {
const token = localStorage.getItem("token");
if (!token) {
setIsLoading(false); // set to false on early exit
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
};
try {
const data = await clientAxios("/usuarios/perfil", config);
setAuth(data);
navigate('/inicio')
} catch (error) {
setAuth({});
} finally {
setIsLoading(false); // set isLoading to false after your async logic is done
}
};
authUser();
}, []);
return (
<AuthContext.Provider
value={{
auth,
setAuth,
isLoading,
}}
>
{children}
</AuthContext.Provider>
);
const ProtectedRoute = () => {
const { auth, isLoading } = useAuth();
if (isLoading) { // don't do any redirects while loading, just wait
return <div>authenticating...</div>;
}
return(
<div>
{auth._id ? (
<div className="bg-gray-100">
<Header/>
<div className="md:min-h-screen">
<main>
<Outlet/>
</main>
</div>
</div>
)
:<Navigate to="/"/> }
</div>
)
};