React-Router v6中如何阻止浏览器返回按钮退至登录页并登出用户
React-Router v6 浏览器返回触发登出问题修复
问题说明
点击浏览器返回按钮时,应用自动跳转至登录页,被误判为未登录状态。需求是仅允许手动点击登出按钮时执行登出,返回按钮点击后停留在主页。
核心代码
Homepage.js
import { AppBar, Toolbar, IconButton, Typography, Box } from '@mui/material'; import { Menu } from '@mui/icons-material'; import { ThemeProvider } from '@mui/material/styles'; import { useLocation } from 'react-router-dom'; import { theme } from '../../App'; import { GeneratorComp } from '../../components/GeneratorComp/generatorcomp'; import { useState } from 'react'; import './home.css'; import { SideMenu } from '../../components/SideMenu/sidemenu'; export function Home() { const location = useLocation(); const [open, setOpen] = useState(false); const handleDrawerOpen = () => { setOpen(true); }; const handleDrawerClose = () => { setOpen(false); }; return ( <ThemeProvider theme={theme}> <Box component='nav' sx={{ flexGrow: 1 }}> <AppBar position='static'> <Toolbar> <IconButton onClick={handleDrawerOpen} size='large'> <Menu fontSize='large' sx={{color: 'white'}}/> </IconButton> <SideMenu open={open} onClose={handleDrawerClose}/> <Typography variant='h4' sx={{ml: 2, mr: 2, flexGrow: 1, textAlign: {xs: 'center', sm: 'start'}}}>Welcome, {location.state?.user?.username}</Typography> </Toolbar> </AppBar> </Box> <Box component='main'> <Typography className="subhead-title" variant="h5" sx={{mt: 3}}> Generate your passwords below! </Typography> <GeneratorComp theme={theme}/> </Box> </ThemeProvider> ) };
Login.js
import Axios from 'axios'; import { Box, AppBar, Toolbar, TextField, Typography, Button } from '@mui/material'; import { ThemeProvider } from '@mui/material/styles'; import { useLocation, useNavigate } from 'react-router-dom'; import { theme } from '../../App'; import { useState, useEffect } from 'react'; export function Login() { const [loginUsername, setLoginUsername] = useState(''); const [loginPassword, setLoginPassword] = useState(''); const [loginStatus, setLoginStatus] = useState(false); const [userInfo, setUserInfo] = useState({}); const navigate = useNavigate(); const location = useLocation(); const login = async () => { await Axios.post('http://localhost:3001/login', { username: loginUsername, password: loginPassword, }).then((res) => { if (!res.data.message) { localStorage.setItem('isLoggedIn', 'true'); localStorage.setItem('user', JSON.stringify(res.data.result[0])); setLoginStatus(true); setUserInfo(res.data.result[0]); } else { document.getElementById('submitMessage').innerHTML = res.data.message; document.getElementById('submitMessage').style.color = 'red'; } }); }; useEffect(() => { if (loginStatus) { navigate('/home', {state: {status: loginStatus, user: userInfo}} ); }; console.log(location.state); }, [navigate,loginStatus, userInfo, location]); return ( <ThemeProvider theme={theme}> <Box sx={{display: 'flex', flexDirection:'column', justifyContent: 'center', alignItems: 'center'}}> <Box component='nav' sx={{ flexGrow: 1, margin: 0, width: '100%' }}> <AppBar position='static'> <Toolbar> <Typography variant='h4' sx={{display: {xs: 'none', sm:'block'}, flexGrow:1, textAlign:'start', letterSpacing: {xs: 'none', sm: '0.4em',md:'0.6em'}}}>Passwordinator</Typography> <Button color='inherit' onClick={() => {navigate('/generator', {state: {status: loginStatus, user: userInfo}})}}>Generator</Button> <Button color='inherit' onClick={() => {navigate('/signup', {state: {status: loginStatus, user: userInfo}})}}>Signup</Button> </Toolbar> </AppBar> </Box> <Box component='form' sx={{display: 'flex', flexDirection: 'column', width: '90%',maxWidth: '500px', m: 4}}> <Typography variant='h2' color='#ffffff' sx={{mb: '70px'}}>Welcome Back!</Typography> <TextField id="input" label="Username" variant="outlined" onChange={(e) => {setLoginUsername(e.target.value)}} sx={{mt: 2, mb: 2}}/> <TextField id="input" label="Password" variant="outlined" onChange={(e) => {setLoginPassword(e.target.value)}} sx={{mt: 2, mb: 10}}/> </Box> <Button color='primary' variant='contained' size='large' onClick={login} sx={{mb:8}}>Login</Button> <Typography id='submitMessage' variant='h6' ></Typography> </Box> </ThemeProvider> ) };
privateroutes.js
import {Outlet, Navigate } from 'react-router-dom'; export const PrivateRoutes = () => { const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true'; return isLoggedIn ? <Outlet /> : <Navigate to='/login' />; }
修复说明
- 问题根源:原代码依赖
location.state判断登录状态,但location.state是路由跳转时的临时内存状态,浏览器返回、页面刷新时会丢失,导致PrivateRoutes判定用户未登录,触发跳转。 - 核心修改:
- 登录成功后将登录状态和用户信息存入
localStorage,持久化保存状态 - PrivateRoutes改为从
localStorage读取登录状态,避免状态丢失 - 移除登录时的
replace: true,保留历史记录,确保返回按钮行为正常
- 登录成功后将登录状态和用户信息存入
- 登出逻辑补充:在Home页的SideMenu或导航栏添加登出按钮,点击时执行:
const handleLogout = () => { localStorage.removeItem('isLoggedIn'); localStorage.removeItem('user'); navigate('/login'); }
内容的提问来源于stack exchange,提问作者Gian Paulo Zamora
相关产品推荐
相关产品推荐

