我遇到了状态问题,当通过 PrivateRoute.jsx 传递时,我的状态没有更新
这是我的App.jsx
const [isUserLogged, setIsUserLogged] = useState(false);
useEffect(() => {
const getUserID = async () => {
try {
await Axios.get("http://localhost:3001/verifylogin").then((res) => {
if (res.data !== "User not found") {
setIsUserLogged(true);
} else {
setIsUserLogged(false);
}
});
} catch (error) {}
};
getUserID();
}, []);
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<RootLayouts />}>
<Route index element={<LandingPage />} />
<CODE BELOW>
</Route>
)
);
return <RouterProvider router={router} />;
代码如下:
我尝试了三件事:
App.jsx 中第一
<Route
path="/admindashboard"
element={!isUserLogged ? <Navigate to="/" /> : <Dashboard />}
/>
PrivateRoutes.jsx 中第二
const PrivateRoute = ({ children, privateRole }) => {
const [isAuthorized, setIsAuthorized] = useState(false);
Axios.defaults.withCredentials = true;
useEffect(() => {
Axios.get("http://localhost:3001/verifylogin").then((res) => {
if (res.data !== "User not found") {
console.log(res.data.role);
setIsAuthorized(res.data.role === privateRole);
} else {
setIsAuthorized(false);
}
});
}, []);
return isAuthorized ? children : <Navigate to="/" />;
};
export default PrivateRoute;
PrivateRoutes.jsx 中的和第三个
const PrivateRoute = ({ children, privateRole, userRole, isUserLogged }) => {
console.log(privateRole, userRole, isUserLogged);
Axios.defaults.withCredentials = true;
useEffect(() => {
Axios.get("http://localhost:3001/verifylogin").then((res) => {
if (res.data !== "User not found") {
setUserDetails(res.data);
} else {
navigate("/");
}
});
}, []);
if (isUserLogged && privateRole === userRole) {
return children;
} else {
return <Navigate to="/" replace />;
}
};
export default PrivateRoute;
/verifylogin 的作用基本上是获取可用的令牌 cookie 并解码 JWT 令牌并检索用户的所有数据(id、角色、名称)。我遇到的问题是它没有通过最新状态,因为我正在使用 useEffect。它只读取第一个/初始状态,因此它始终为 false 或 null。我该如何解决这个问题?
您面临的问题主要是由于您如何尝试使用 React Router v6 在 React 应用程序中管理身份验证和基于角色的访问控制。状态更新和 HTTP 请求的异步性质与 React 中的初始渲染行为相结合,可能会导致组件决定渲染内容时状态尚未更新的情况。
以下是一些改进和解决
PrivateRoute
组件和整体身份验证流程问题的策略:
PrivateRoute
中的身份验证状态,而是考虑使用全局上下文 (AuthContext
) 来管理用户身份验证和角色。这样,您的身份验证逻辑就位于一个位置,您可以轻松访问整个应用程序中的用户状态。首先,创建身份验证上下文:
// AuthContext.js
import React, { createContext, useContext, useState, useEffect } from 'react';
import Axios from 'axios';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [isUserLogged, setIsUserLogged] = useState(false);
const [userRole, setUserRole] = useState(null);
useEffect(() => {
Axios.defaults.withCredentials = true;
const verifyLogin = async () => {
try {
const res = await Axios.get("http://localhost:3001/verifylogin");
if (res.data !== "User not found") {
setIsUserLogged(true);
setUserRole(res.data.role);
} else {
setIsUserLogged(false);
setUserRole(null);
}
} catch (error) {
console.error('Authentication error:', error);
}
};
verifyLogin();
}, []);
return (
<AuthContext.Provider value={{ isUserLogged, userRole }}>
{children}
</AuthContext.Provider>
);
};
// Custom hook to use auth context
export const useAuth = () => useContext(AuthContext);
App.jsx
以使用 AuthProvider
用 AuthProvider
:// In App.jsx
import { AuthProvider } from './AuthContext';
const App = () => {
return (
<AuthProvider>
<RouterProvider router={router} />
</AuthProvider>
);
};
PrivateRoute
以使用 AuthContext
修改你的 PrivateRoute
以直接消耗 AuthContext
。这样,它就会自动对身份验证状态的变化做出反应:// PrivateRoute.jsx
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from './AuthContext';
const PrivateRoute = ({ children, privateRole }) => {
const { isUserLogged, userRole } = useAuth();
if (!isUserLogged) {
// User not logged in
return <Navigate to="/" replace />;
} else if (privateRole && privateRole !== userRole) {
// User doesn't have the right role
return <Navigate to="/unauthorized" replace />;
}
return children; // User is logged in and has the right role (if specified)
};
export default PrivateRoute;
PrivateRoute
您现在可以在路由配置中使用 PrivateRoute
,知道它将正确响应身份验证状态和用户角色的更改:// Inside your router configuration
<Route path="/admindashboard" element={
<PrivateRoute privateRole="admin">
<Dashboard />
</PrivateRoute>
}/>