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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:15:03