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. 易于扩展:支持添加注册/找回密码等衍生功能
相关推荐
niucloud-admin1 分钟前
NIUCLOUD PHP SAAS上门家政使用文档:客户报表
前端·人工智能·ai编程
漫步人生走在路上6 分钟前
外贸B2B行业GEO优化白皮书:从0到1完整落地流程
前端·人工智能·搜索引擎·chatgpt·twitter
程序猿乐锅30 分钟前
面向对象练习:图书借阅系统里的封装、抽象类与多态
开发语言·前端·数据库·后端·python·学习·1024程序员节
老木避暑研究所34 分钟前
负氧离子数据的采集、清洗与可视化:一套可复用的环境监测数据分析模板
前端·人工智能·数据分析
计算机魔术师1 小时前
AI 推翻了一个当了30年地基的假设,查的人恰好是立论者
前端
范同学~1 小时前
项目添加水印
java·前端·javascript
鬼手点金1 小时前
缓存作为架构约束
开发语言·前端·人工智能·python·microsoft·缓存·架构
月夏1 小时前
前端第一次接自己的后端:mock 切真接口踩了 2 个坑,组件只改了一行 import
前端·后端·node.js
IT_陈寒1 小时前
React实战技巧总结:如何优雅处理复杂列表的渲染卡顿问题?
前端·人工智能·后端
ZoeLandia1 小时前
React11:数据请求与 TanStack Query
前端·react.js·typescript·react