我有一个简单的应用程序,我试图让未经身份验证的用户无法访问它的管理部分。我遇到的问题是下面的 userAuth
始终设置为 false,因此受保护的路由无法访问,即使对于使用适当的 localStorage 身份验证登录的用户也是如此。我的想法是 userAuth 被设置为 false 作为其初始状态,并且当对受保护的路由 URL 发出请求时,useEffect 不会覆盖它。
以下是我目前的路由:
import { lazy, Suspense, useEffect, useState } from 'react';
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
import * as ROUTES from './constants/routes';
import ProtectedRoute from './helpers/ProtectedRoute';
const Admin = lazy(() => import('./screens/Admin'));
const Home = lazy(() => import('./screens/Home'));
const Login = lazy(() => import('./screens/Login'));
const NotFound = lazy(() => import('./screens/NotFound'));
const Signup = lazy(() => import('./screens/Signup'));
const App = () => {
const [userAuth, setUserAuth] = useState(false);
useEffect(() => {
const checkUser = () => {
const user = localStorage.getItem("userAuth");
if (user) {
setUserAuth(true);
} else {
setUserAuth(false);
}
}
checkUser();
}, []);
return (
<Router>
<Suspense fallback={<p>Loading ...</p>}>
<Switch>
<Route path={ROUTES.HOME} component={Home} exact={true} />
<Route userAuth={userAuth} path={ROUTES.LOGIN} component={Login} />
<Route userAuth={userAuth} path={ROUTES.SIGNUP} component={Signup} />
<ProtectedRoute userAuth={userAuth} path={ROUTES.ADMIN} exact>
<Admin />
</ProtectedRoute>
<Route component={NotFound} />
</Switch>
</Suspense>
</Router>
);
}
export default App;
我有一个 useEffect 来判断用户是否已通过身份验证。这是在用户为登录页面提交成功的 POST 请求时设置的:
import { useEffect, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import Header from '../components/Header';
import * as ROUTES from '../constants/routes';
const Login = ({setUserAuth}) => {
const history = useHistory();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [loginErr, setLoginErr] = useState(false);
const isUsernameValid = username.length > 0;
const isPasswordValid = (password !== '' && password.length > 5);
const isLoginValid = isUsernameValid && isPasswordValid;
useEffect(() => {
document.title = 'Login';
}, []);
const logIn = async (e, username, password) => {
e.preventDefault();
const data = {username, password}
const formData = JSON.stringify(data);
try {
const req = await fetch(
"http://localhost:3000/api/login",
{
method: "POST",
body: formData,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}
);
const myJson = await req.json();
if (req.status !== 200) {
setLoginErr(true);
return;
}
localStorage.setItem("token", myJson.token);
localStorage.setItem("userAuth", true);
setUserAuth(true);
} catch (err) {
setLoginErr(true);
}
}
return (
<div className="bg-gray-100">
<div className="container h-screen mx-auto flex flex-col flex-wrap">
<div className="container border rounded m-auto w-1/4 flex flex-wrap justify-center bg-white shadow-new">
<h1 className="text-3xl m-4">Blog.</h1>
<form className="flex flex-wrap justify-center" onSubmit={(e) => logIn(e, username, password)}>
<input
className="border rounded w-9/12 p-1 mb-2 pl-2 bg-gray-100 outline-none"
name="username"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
className="border rounded w-9/12 p-1 mb-4 pl-2 bg-gray-100 outline-none"
name="password"
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
className={`border rounded w-9/12 p-1 mb-4 bg-blue-400 text-white font-medium ${isLoginValid ? "cursor-pointer" : "bg-opacity-50 cursor-default"}`}
type="submit"
>Log In</button>
<div className="mb-4">
<p>Don't have an account? <Link to={ROUTES.SIGNUP} className="font-medium text-blue-500 cursor-pointer">Sign up</Link></p>
</div>
{loginErr && <p className="text-xs text-red-600">Error with login information</p>}
</form>
</div>
</div>
</div>
)
}
export default Login;
这是受保护的路由:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import * as ROUTES from '../constants/routes';
const ProtectedRoute = ({ userAuth, children, ...rest }) => {
return (
<Route
{...rest}
render={({ location }) => {
if (userAuth && userAuth !== null) {
console.log(userAuth)
console.log("FIRST")
// Using cloneElement to add / modify the props of its children.
return React.cloneElement(children, { userAuth });
}
if (!userAuth || userAuth === null) {
console.log(userAuth)
console.log("SECOND")
return (
<Redirect
to={{
pathname: ROUTES.LOGIN,
state: { from: location }
}}
/>
);
}
return null;
}}
/>
)
}
export default ProtectedRoute;
任何建议将不胜感激。
非常感谢!
答案 0 :(得分:1)
问题似乎是 Login
组件没有接收 setUserAuth
作为道具,因为它是由 Route
道具上的标准 component
组件呈现的,这意味着它只是通过路由道具。
在 Login
道具上渲染 render
并传入额外的道具。这将允许登录页面更新私有路由的父组件中的 userAuth
状态。
<Route
path={ROUTES.LOGIN}
render={routeProps => (
<Login
{...routeProps}
setUserAuth={setUserAuth}
/>
)}
/>
移除挂载 useEffect
钩子并使用状态初始化函数从 localStorage 设置初始 userAuth
状态,使其在初始渲染时可用。
const [userAuth, setUserAuth] = useState(() => {
const user = localStorage.getItem("userAuth");
return !!user;
});
答案 1 :(得分:0)
您的 useEffect
钩子检查用户是否仅在组件安装时登录。您应该将 const user = localStorage.getItem("userAuth")
移到钩子之外,然后在钩子的依赖数组中添加 user
,这样代码就会在依赖项的每次更改时运行(这是用户成功登录后会发生的情况)在)
const user = localStorage.getItem("userAuth");
useEffect(() => {
const checkUser = () => {
if (user) {
setUserAuth(true);
} else {
setUserAuth(false);
}
}
checkUser();
}, [user]);