React通用登錄/注銷功能實現方案(基于shadcn/ui)
- 一、功能需求分析
- 二、通用功能封裝
- 1. 通用登錄表單組件
- 2. 認證Hook封裝
- 三、功能使用示例
- 1. 登錄頁面實現
- 2. 用戶菜單實現
- 四、路由保護實現
- 五、方案優勢
一、功能需求分析
需要實現以下核心功能:
- 登錄表單組件
- 登錄狀態管理
- 用戶注銷功能
- 路由權限控制
二、通用功能封裝
1. 通用登錄表單組件
// 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?: stringtitle: stringdescription?: stringerror?: stringfields: FormField[]submitText?: stringonSubmit: (data: Record<string, string>) => voidchildren?: ReactNode
}export type FormField = {name: stringlabel: stringtype?: stringplaceholder?: stringrequired?: 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><Inputid={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封裝
// 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. 登錄頁面實現
// 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"><AuthFormtitle="Login to System"description="Enter your credentials to continue"fields={loginFields}onSubmit={login}error={error}submitText="Sign In"/></div></div>)
}
2. 用戶菜單實現
// 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>)
}
四、路由保護實現
// 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 />
}
五、方案優勢
- 高度可配置:表單字段、驗證邏輯均可自定義
- 類型安全:完善的TypeScript類型定義
- UI解耦:業務邏輯與UI組件分離
- 易于擴展:支持添加注冊/找回密碼等衍生功能