React通用登录/注销功能实现方案(基于shadcn/ui)

React通用登录/注销功能实现方案(基于shadcn/ui)

一、功能需求分析

需要实现以下核心功能:

  1. 登录表单组件
  2. 登录状态管理
  3. 用户注销功能
  4. 路由权限控制

二、通用功能封装

1. 通用登录表单组件

tsx 复制代码
// lib/components/auth-form.tsx
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { FormEvent, ReactNode } from "react"

interface AuthFormProps {
  className?: string
  title: string
  description?: string
  error?: string
  fields: FormField[]
  submitText?: string
  onSubmit: (data: Record<string, string>) => void
  children?: ReactNode
}

export type FormField = {
  name: string
  label: string
  type?: string
  placeholder?: string
  required?: boolean
}

export function AuthForm({
  className,
  title,
  description,
  error,
  fields,
  submitText = "Submit",
  onSubmit,
  children
}: AuthFormProps) {
  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    const formData = new FormData(e.currentTarget)
    const data = Object.fromEntries(formData.entries())
    onSubmit(Object.fromEntries(
      Object.entries(data).map(([key, value]) => [key, value.toString()])
    ))
  }

  return (
    <div className={cn("flex flex-col gap-6", className)}>
      <Card>
        <CardHeader>
          <CardTitle>{title}</CardTitle>
          {description && <CardDescription>{description</CardDescription>}
        </CardHeader>
        <CardContent>
          <form onSubmit={handleSubmit}>
            <div className="flex flex-col gap-6">
              {error && (
                <div className="text-sm font-medium text-destructive">
                  {error}
                </div>
              )}

              {fields.map((field) => (
                <div key={field.name} className="grid gap-3">
                  <Label htmlFor={field.name}>{field.label}</Label>
                  <Input
                    id={field.name}
                    name={field.name}
                    type={field.type || "text"}
                    required={field.required !== false}
                    placeholder={field.placeholder}
                  />
                </div>
              ))}

              <Button type="submit" className="w-full">
                {submitText}
              </Button>
            </div>
          </form>
          {children}
        </CardContent>
      </Card>
    </div>
  )
}

2. 认证Hook封装

tsx 复制代码
// lib/hooks/use-auth.ts
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'

export const useAuth = () => {
  const [error, setError] = useState('')
  const navigate = useNavigate()

  const login = async (credentials: Record<string, string>) => {
    try {
      // 示例验证逻辑,实际替换为API调用
      if (credentials.username === 'admin' && credentials.password === '123456') {
        localStorage.setItem('isAuthenticated', 'true')
        navigate('/')
      } else {
        setError('Invalid credentials')
      }
    } catch (err) {
      setError('Login failed')
    }
  }

  const logout = () => {
    localStorage.removeItem('isAuthenticated')
    navigate('/login')
  }

  return { login, logout, error }
}

三、功能使用示例

1. 登录页面实现

tsx 复制代码
// app/login/page.tsx
import { AuthForm } from "@/lib/components/auth-form"
import { useAuth } from "@/lib/hooks/use-auth"

export default function LoginPage() {
  const { login, error } = useAuth()

  const loginFields = [
    { name: "username", label: "Username", required: true },
    { name: "password", label: "Password", type: "password", required: true }
  ]

  return (
    <div className="flex h-screen items-center justify-center bg-gray-100 p-4">
      <div className="w-full max-w-md">
        <AuthForm
          title="Login to System"
          description="Enter your credentials to continue"
          fields={loginFields}
          onSubmit={login}
          error={error}
          submitText="Sign In"
        />
      </div>
    </div>
  )
}

2. 用户菜单实现

tsx 复制代码
// components/nav-user.tsx
import { useAuth } from "@/lib/hooks/use-auth"

export function NavUser() {
  const { logout } = useAuth()
  
  return (
    <DropdownMenu>
      {/* 其他菜单项 */}
      <DropdownMenuItem onClick={logout}>
        <LogOut />
        Log out
      </DropdownMenuItem>
    </DropdownMenu>
  )
}

四、路由保护实现

tsx 复制代码
// router.ts
import { Navigate } from 'react-router-dom'

const PrivateRoute = ({ children }: { children: JSX.Element }) => {
  const isAuthenticated = localStorage.getItem('isAuthenticated')
  return isAuthenticated ? children : <Navigate to="/login" replace />
}

五、方案优势

  1. 高度可配置:表单字段、验证逻辑均可自定义
  2. 类型安全:完善的TypeScript类型定义
  3. UI解耦:业务逻辑与UI组件分离
  4. 易于扩展:支持添加注册/找回密码等衍生功能
相关推荐
达令哥1 小时前
告别 ARouter!基于 Google 官方 Navigation 3 + KSP 打造 Compose 时代的双轨制路由框架
android·前端
枫叶丹41 小时前
MCP、A2A、AG-UI:一篇讲清 Agent 协议栈
人工智能·ui·chatgpt·agent·codex
ITmaster07311 小时前
前端 AI 面试题:高频考点与实战解析
前端·人工智能
赵大仁1 小时前
Structured Output 落地:JSON Schema、重试与前端校验
前端·ai·大模型·工程化·json schema
李剑一1 小时前
有点干,前端架构基础之:Web Worker到底是什么?它和Java中的线程是一个道理吗?
前端·面试·架构
爱勇宝3 小时前
《道德经》第 10 章:真正成熟的人,能成事但不控制一切
前端·后端·程序员
看谷秀3 小时前
arkts- 8 三方库介绍
前端·arkts
六边形6663 小时前
独立开发不知道做什么?使用 TRAE Work 抓取差评痛点,快速跑通产品立项流
前端·后端·面试
亿元程序员3 小时前
小伙伴发我一个 1G 的 Cocos 项目,assets 只有几十兆
前端
paopaokaka_luck3 小时前
基于springboot3+vue3的文山民族文化资源展示与推广平台(协同过滤算法、Echarts 图形化分析)
java·前端·spring boot·学习·echarts