React/Next.js实现登录后头部按钮切换及侧边栏权限控制
解决React + Next.js登录状态同步及侧边栏控制问题
核心思路
- 通过React Context管理全局登录状态,实现LoginForm与Header组件的状态共享
- 根据登录状态动态切换Header的按钮文本与功能
- 控制MUI侧边栏的显示权限,仅登录后可见
步骤1:创建全局登录状态上下文(AuthContext)
创建app/contexts/AuthContext.jsx文件:
"use client"; import { createContext, useContext, useState } from "react"; const AuthContext = createContext(); export function AuthProvider({ children }) { const [isLoggedIn, setIsLoggedIn] = useState(false); const login = () => setIsLoggedIn(true); const logout = () => setIsLoggedIn(false); return ( <AuthContext.Provider value={{ isLoggedIn, login, logout }}> {children} </AuthContext.Provider> ); } export function useAuth() { return useContext(AuthContext); }
步骤2:在根布局中引入AuthProvider
修改app/layout.jsx,将整个应用包裹在AuthProvider中:
import { AuthProvider } from "./contexts/AuthContext"; // 其他导入... export default function RootLayout({ children }) { return ( <html lang="en"> <body> <AuthProvider>{children}</AuthProvider> </body> </html> ); }
步骤3:修改LoginForm组件,使用全局状态
更新登录逻辑,触发全局登录状态变更:
import Dashboard from "@/app/(routes)/dashboard/page"; import { LockOutlined } from "@mui/icons-material"; import { Container, CssBaseline, Box, Avatar, Typography, TextField, Button, Grid, } from "@mui/material"; import Link from "next/link"; import { useState } from "react"; import { useAuth } from "@/app/contexts/AuthContext"; const LoginForm = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const { isLoggedIn, login, logout } = useAuth(); const handleLogin = () => { if (email === 'aa@yahoo.com' && password === 'aaa') { login(); alert('Login successful!'); } else { alert('Invalid email or password'); } console.log('Email:', email); console.log('Password:', password); }; const handleLogout = () => { logout(); setEmail(''); setPassword(''); }; if (isLoggedIn) { return <Dashboard/>; } return ( <> <Container maxWidth="xs"> <CssBaseline /> <Box sx={{ mt: 20, display: "flex", flexDirection: "column", alignItems: "center", }} > <Avatar sx={{ m: 1, bgcolor: "primary.light" }}> <LockOutlined /> </Avatar> <Typography variant="h5">Login</Typography> <Box sx={{ mt: 1 }}> <TextField margin="normal" required fullWidth id="email" label="Email Address" name="email" autoFocus value={email} onChange={(e) => setEmail(e.target.value)} /> <TextField margin="normal" required fullWidth id="password" name="password" label="Password" type="password" value={password} onChange={(e) => { setPassword(e.target.value); }} /> <Button fullWidth variant="contained" sx={{ mt: 3, mb: 2 }} onClick={handleLogin} > Login </Button> <Grid container justifyContent="flex-end"> <Grid item> <Link href="/signup" passHref> Don't have an account? Register </Link> </Grid> </Grid> </Box> </Box> </Container> </> ); }; export default LoginForm;
步骤4:修改Header组件,根据登录状态控制UI
更新Header,实现按钮切换和侧边栏权限控制:
"use client" import * as React from 'react'; import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import MenuIcon from '@mui/icons-material/Menu'; import Drawer from '@mui/material/Drawer'; import SideNav from '../SideNav/SideNav'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { styled, useTheme } from '@mui/material/styles'; import { useAuth } from "@/app/contexts/AuthContext"; type Anchor = 'left' ; const drawerWidth = 240; const DrawerHeader = styled('div')(({ theme }) => ({ display: 'flex', alignItems: 'center', padding: theme.spacing(0, 1), ...theme.mixins.toolbar, justifyContent: 'flex-end', })); interface AppBarProps extends MuiAppBarProps { open?: boolean; } const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== 'open', })<AppBarProps>(({ theme, open }) => ({ transition: theme.transitions.create(['margin', 'width'], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), ...(open && { width: `calc(100% - ${drawerWidth}px)`, marginLeft: `${drawerWidth}px`, transition: theme.transitions.create(['margin', 'width'], { easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen, }), }), })); export default function Header() { const theme = useTheme(); const [open, setOpen] = React.useState(false); const { isLoggedIn, logout } = useAuth(); const handleDrawerOpen = () => { setOpen(true); }; const handleDrawerClose = () => { setOpen(false); }; return ( <Box sx={{ flexGrow: 1 }}> <AppBar position="static" open={open} sx={{ bgcolor: 'primary.dark', color: 'secondary.dark'}}> <Toolbar> {isLoggedIn && ( <IconButton size="large" edge="start" color="inherit" aria-label="menu" sx={{ mr: 2, ...(open && { display: 'none' })}} onClick={handleDrawerOpen} > <MenuIcon /> </IconButton> )} <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}> News </Typography> {isLoggedIn && ( <Drawer open={open}> <DrawerHeader > <IconButton onClick={handleDrawerClose}> {theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />} </IconButton> </DrawerHeader> <SideNav/> </Drawer> )} {isLoggedIn ? ( <Button color="inherit" onClick={logout}>Logout</Button> ) : ( <Button color="inherit" href="/login">Login</Button> )} </Toolbar> </AppBar> </Box> ); }
关键改动说明
- 全局状态管理:通过AuthContext实现登录状态的跨组件共享,解决组件间状态隔离问题
- 按钮动态切换:Header组件根据
isLoggedIn状态渲染登录/注销按钮,并绑定对应逻辑 - 侧边栏权限控制:仅登录用户可见菜单按钮和侧边栏,未登录时隐藏相关元素
- 登录逻辑联动:LoginForm验证通过后调用全局
login方法,同步更新Header状态
内容的提问来源于stack exchange,提问作者user17449555
相关产品推荐
相关产品推荐

