You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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' />;
}

修复说明

  1. 问题根源:原代码依赖location.state判断登录状态,但location.state是路由跳转时的临时内存状态,浏览器返回、页面刷新时会丢失,导致PrivateRoutes判定用户未登录,触发跳转。
  2. 核心修改:
    • 登录成功后将登录状态和用户信息存入localStorage,持久化保存状态
    • PrivateRoutes改为从localStorage读取登录状态,避免状态丢失
    • 移除登录时的replace: true,保留历史记录,确保返回按钮行为正常
  3. 登出逻辑补充:在Home页的SideMenu或导航栏添加登出按钮,点击时执行:
    const handleLogout = () => {
        localStorage.removeItem('isLoggedIn');
        localStorage.removeItem('user');
        navigate('/login');
    }
    

内容的提问来源于stack exchange,提问作者Gian Paulo Zamora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 20:15:17