我正在开发一个带有用户身份验证的 React 应用程序。我想在用户注销时(即,当他们的身份验证令牌被删除时)自动将用户重定向到登录页面。但是,在我手动刷新页面之前,应用程序不会重定向。
这是我的代码的相关部分:
默认布局.jsx:
import {React, useEffect} from "react";
import { Navigate, Outlet,useNavigate } from "react-router-dom";
import { useStateContext } from "../contexts/ContextProvider";
import axiosClient from "../axios-client.js";
import SideBar from "./nav/SideBar";
import Header from "./nav/Header";
const DefaultLayout = () => {
const { user, token, isAuthenticated, setUser, setToken } = useStateContext();
if (!token) {
return <Navigate to="/login" />;
}
const onLogout = (ev) => {
ev.preventDefault()
axiosClient.post('/logout')
.then(() => {
window.location.reload()
setUser({})
setToken(null)
})
}
};
ContextProvider:
import { createContext, useContext, useState } from "react";
const StateContext = createContext({
currentUser: null,
token: null,
notification: null,
setUser: () => {},
setToken: () => {},
setNotification: () => {}
})
export const ContextProvider = ({children}) => {
const [user, setUser] = useState({});
const [token, _setToken] = useState(localStorage.getItem('ACCESS_TOKEN'));
const [notification, _setNotification] = useState('');
const setToken = (token, callback) => {
_setToken(token)
if (token) {
localStorage.setItem('ACCESS_TOKEN', token);
} else {
localStorage.removeItem('ACCESS_TOKEN');
}
}
}
App.js:
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Sidebar from "./components/nav/SideBar.jsx";
import Header from "./components/nav/Header.jsx";
import Dashboard from "./views/Dashboard";
import GuestLayout from './components/GuestLayout.jsx'
import { useStateContext } from "./contexts/ContextProvider";
import Users from "./views/customers/Users.jsx";
import ListCustomers from "./views/customers/ListCustomers.jsx";
import {Outlet} from "react-router-dom";
const App = () => {
return (
<Router>
<div className="page-wrapper">
<Sidebar />
<div className="body-wrapper">
<Header />
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="users" element={<Users />}>
<Route index element={<Outlet />}>
<Route path="list-customers" element={<ListCustomers />} />
</Route>
</Route>
</Routes>
</div>
</div>
</Router>
);
};
标头组件中的注销按钮:
import React, {useEffect} from "react";
export default function Header({ user, onLogout }) {
return (
<header className="app-header">
<nav className="navbar navbar-expand-lg navbar-light">
<ul className="navbar-nav">
<li className="nav-item d-block d-xl-none">
<a className="nav-link sidebartoggler nav-icon-hover" id="headerCollapse" href="javascript:void(0)">
<i className="ti ti-menu-2"></i>
</a>
</li>
<li className="nav-item">
<a className="nav-link nav-icon-hover" href="javascript:void(0)">
<i className="ti ti-bell-ringing"></i>
<div className="notification bg-primary rounded-circle"></div>
</a>
</li>
</ul>
{user.name}
<button onClick={onLogout} className="btn btn-primary">Logout</button>
</nav>
</header>
);
}
我尝试了多种解决方案,包括使用
setToken
的回调,将导航逻辑移至 setToken
中的 ContextProvider
函数中,以及在 App.js
文件中添加条件以渲染 DefaultLayout
组件仅当用户通过身份验证时。然而,这些解决方案要么不起作用,要么导致刷新无限循环。
我是一名初学者,我正在按照 YouTube 教程构建这个项目。我不确定为什么相同的代码在其他应用程序中有效,但在我的应用程序中无效。任何帮助将不胜感激。
谢谢!
我尝试通过建议不同的方法来解决这个问题。我期望这些解决方案之一能够解决问题,并且当身份验证令牌被删除时,应用程序会自动重定向到登录页面。然而,尽管尝试了多种解决方案,问题仍然存在。应用程序不会在注销后立即重定向到登录页面,而是需要刷新手动页面。在某些情况下,建议的解决方案会导致无限循环的刷新。这不是预期的结果,我目前正在寻找此问题的替代解决方案。
我怀疑正是这个
window.location.reload()
中断了从localStorage中清除令牌,因此当页面重新加载时,之前存储的任何值都会保留。我怀疑您还在其他地方添加了一些额外的逻辑,当应用程序安装时由于重新加载而验证令牌,然后由于令牌已失效而起作用。
我建议删除
window.location.reload()
,以便应用程序保持安装状态,并将重定向逻辑移至注销处理程序中。我建议还将身份验证逻辑集中到上下文提供程序中。
示例:
const StateContext = createContext({
currentUser: null,
token: null,
notification: null,
setUser: () => {},
setToken: () => {},
setNotification: () => {},
logout: () => {},
})
export const ContextProvider = ({ children }) => {
const navigate = useNavigate();
const [user, setUser] = useState(null);
const [token, setToken] = useState(() => {
return JSON.parse(localStorage.getItem('ACCESS_TOKEN'));
});
const [notification, _setNotification] = useState(null);
useEffect(() => {
if (token) {
localStorage.setItem('ACCESS_TOKEN', JSON.stringify(token));
} else {
localStorage.removeItem('ACCESS_TOKEN');
}
}, [token]);
const logoutHandler = () => {
axiosClient.post('/logout')
.then(() => {
setUser(null);
setToken(null);
})
.then(() => {
navigate("/login", { replace: true });
})
.catch(error => {
// catch/handle any thrown errors or Promise rejections
});
};
...
const value = {
currentUser,
token,
notification,
setUser,
setToken,
setNotification,
logout,
};
return (
<StateContext.Provider value={value}>
{children}
</StateContext>
);
}
const DefaultLayout = () => {
const {
user,
token,
isAuthenticated,
logout,
} = useStateContext();
const onLogout = (ev) => {
ev.preventDefault();
logout();
}
...
};