我希望根据用户本地存储中是否存在令牌,有条件地将经过身份验证的路由包含在我的 ReactJS 应用程序的路由索引中。但是,要实现此目的,必须在重定向之前在登录时重新评估路由。
以下是理想的使用场景:
当用户登录时(由
handleLogin
函数处理),浏览器的本地存储中会设置一个令牌,然后用户将被重定向到 /dashboard
。
router
中的三元运算符有条件地在接受的路由中包含私有页面,例如,旨在让经过身份验证的用户进入其仪表板。
问题:
在设置令牌和用户重定向之间,路由似乎没有更新。这意味着,用户登录后会遇到错误,因为仪表板还不是有效路径(即使应该是有效路径)。
相关代码片段:
在
LoginPage.jsx
:
const handleLogin = () => {
console.log(
`Sending login request to ${process.env.REACT_APP_API_URL}/api/auth`,
);
fetch(`${process.env.REACT_APP_API_URL}/api/auth`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email, password }),
})
.then((response) => response.json())
.then((data) => {
if (data.message === "success") {
// the login was successful
setToken(data.token);
console.log("successful login");
navigate("/dashboard");
// window.alert("The login was successful");
// get value of token using useAuth
} else {
window.alert(
`The login failed with the following error:\n\n${data.error}`,
);
}
})
.catch((error) => {
console.error("Could not login: ", error);
window.alert("An error occurred while trying to login.");
});
};
在
index.jsx
:
const Routes = () => {
const { token } = useAuth();
const [ isAuthenticated, setIsAuthenticated ] = useState(!!token);
useEffect(() => {
setIsAuthenticated(!!token);
}, [token])
// route configurations go here
const routesAuthenticated = [
{
path: "/",
element: <ProtectedRoute />,
children: [
{
path: "/",
element: <UserDashboardPage />
},
{
path: "/dashboard",
element: <UserDashboardPage />
},
{
path: "/logout",
element: <LogoutPage />
},
]
}
];
const routesUnauthenticated = [
{
path: "/",
element: <LandingPage />
},
{
path: "/login",
element: <LoginPage />
},
{
path: "/about",
element: <AboutPage />
},
{
path: "/ipsum",
element: <IpsumPage />
}
];
// decide which routes are available to user based on authentication status
const router = createBrowserRouter([
// we use the ... operator to combine these arrays into one
...routesUnauthenticated,
...(isAuthenticated ? routesAuthenticated : [])
]);
// provide configuration using RouterProvider
return <RouterProvider router={router} />;
};
尝试修复:
我尝试使用以下代码强制登录时重新加载路由
useEffect(() => {
setIsAuthenticated(!!token);
}, [token])
不幸的是,这并不能保证在
handleLogin
将用户重定向到其仪表板之前重新加载路线。
感谢您的宝贵时间。感谢所有帮助!
基本问题是您正在有条件地渲染路线,并且正如您所发现的,当您尝试导航到目标路线时,目标路线尚未安装且无法匹配。解决方案是**无条件渲染所有路由并相应地保护它们。
建议:
ProtectedRoute
以确保仅允许经过身份验证的用户访问,否则重定向到安全且不受保护的路线。AnonymousRoute
组件以确保仅允许未经身份验证的用户访问,否则重定向到任何安全的非匿名路由。const ProtectedRoute = () => {
const { token } = useAuth();
const location = useLocation();
return !!token
? <Outlet />
: <Navigate to="/login" replace state={{ from: location }} />;
};
const AnonymousRoute = () => {
const { token } = useAuth();
return !!token
? <Navigate to="/" replace />
: <Outlet />;
};
const Wrapper = ({
authenticatedElement = null,
unauthenticatedElement = null,
}) => {
const { token } = useAuth();
return !!token ? authenticatedElement : unauthenticatedElement
}
const router = createBrowserRouter([
{
path: "/",
element: (
<Wrapper
authentictedElement={<UserDashboardPage />}
unauthentictedElement={<LandingPage />}
/>
),
},
{
element: <ProtectedRoute />,
children: [
{
path: "/dashboard",
element: <UserDashboardPage />
},
{
path: "/logout",
element: <LogoutPage />
},
// ... other protected authenticated routes ...
]
},
{
element: <AnonymousRoute />,
children: [
{
path: "/login",
element: <LoginPage />
},
// ... other protected unauthenticated routes ...
]
},
{
path: "/about",
element: <AboutPage />
},
{
path: "/ipsum",
element: <IpsumPage />
},
// ... other unprotected routes ...
]);
const Routes = () => {
return <RouterProvider router={router} />;
};