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. 易于扩展:支持添加注册/找回密码等衍生功能
相关推荐
Easonmax1 分钟前
零基础入门 React Native 鸿蒙跨平台开发:8——固定表头和列的复杂表格
react native·react.js·harmonyos
We་ct2 分钟前
LeetCode 151. 反转字符串中的单词:两种解法深度剖析
前端·算法·leetcode·typescript
yinmaisoft43 分钟前
JNPF 表单模板实操:高效复用表单设计指南
前端·javascript·html
37方寸1 小时前
前端基础知识(JavaScript)
开发语言·前端·javascript
信徒favor2 小时前
我用AI一次性生成3种UI原型(附提示词)
ui
json{shen:"jing"}2 小时前
1. 两数之和
前端·javascript·数据库
github.com/starRTC2 小时前
Claude Code中英文系列教程19:使用subagent子代理与创建自定义子代理【重要】
前端·javascript·数据库
hua_ban_yu2 小时前
vue3 + ts 制作指令,防止按钮在固定时间内重复点击,不会影响到表单的校验
前端·javascript·vue.js
老神在在0012 小时前
Token身份验证完整流程
java·前端·后端·学习·java-ee
利刃大大3 小时前
【Vue】指令修饰符 && 样式绑定 && 计算属性computed && 侦听器watch
前端·javascript·vue.js·前端框架