淺談React-router v6 實現登錄驗證流程
此示例演示瞭一個包含三個頁面的簡單登錄流程:公共頁面、受保護頁面和登錄頁面。 為瞭查看受保護的頁面,你必須先登錄。
首先,訪問公共頁面。 然後,訪問受保護的頁面。 你尚未登錄,因此你將被重定向到登錄頁面。 登錄後,你將被重定向回受保護的頁面。
封裝 Context 包裹容器
首先封裝AuthProvider
組件,利用Context
特性共享那些對於一個組件樹而言是“全局”的數據。
全局定義user
、signIn
、signOut
數據和方法,signIn
、signOut
使用瞭高階函數,也方便後續擴展和修改。
Context
主要應用場景在於很多不同層級的組件需要訪問同樣一些的數據。請謹慎使用,因為這會使得組件的復用性變差。
如果你隻是想避免層層傳遞一些屬性,組件組合(component composition)有時候是一個比Context
更好的解決方案。
import { ReactNode, createContext, useState } from "react"; export interface AuthContextType { user: any; signIn: (user: string, callback: VoidFunction) => void; signOut: (callback: VoidFunction) => void; } export let AuthContext = createContext<AuthContextType | null>(null); const fakeAuthProvider = { isAuthenticated: false, signIn(callback: VoidFunction) { this.isAuthenticated = true; setTimeout(callback, 100); }, signOut(callback: VoidFunction) { this.isAuthenticated = false; setTimeout(callback, 100); }, }; const AuthProvider = ({ children }: { children: ReactNode }) => { const [user, setUser] = useState<any>(null); let signIn = (newUser: string, callback: VoidFunction) => { return fakeAuthProvider.signIn(() => { setUser(newUser); callback(); }); }; let signOut = (callback: VoidFunction) => { return fakeAuthProvider.signOut(() => { setUser(null); callback(); }); }; return ( <AuthContext.Provider value={{ user, signIn, signOut }}> {children} </AuthContext.Provider> ); }; export default AuthProvider;
封裝 Layout 父級容器
Layout
組件主要是針對登錄狀態進行校驗,然後做相應處理。利用react-router
v6中<Outlet />
組件顯示嵌套路由,相比於v5版本v6實現嵌套路由更加方便,省略瞭很多冗餘的判斷代碼。
import { useContext } from "react"; import { useNavigate, Link, Outlet } from "react-router-dom"; import { AuthContext, AuthContextType } from "../AuthProvider"; const useAuth = () => useContext(AuthContext); const AuthStatus = () => { let auth = useAuth(); let { user, signOut } = auth as AuthContextType; let navigate = useNavigate(); if (!user) return <p>沒有登錄</p>; return ( <> <p>你好 {user}! </p> <button onClick={() => signOut(() => navigate("/"))}>退出</button> </> ); }; const Layout = () => { return ( <div> <AuthStatus /> <ul> <li> <Link to="/">公共頁面</Link> </li> <li> <Link to="/protected">受保護頁面</Link> </li> </ul> <Outlet /> </div> ); }; export default Layout;
開發 Login 模塊
import { useContext, FormEvent } from "react"; import { useNavigate, useLocation, Location } from "react-router-dom"; import { AuthContext, AuthContextType } from "../AuthProvider"; interface State extends Omit<Location, "state"> { state: { from: { pathname: string; }; }; } const useAuth = () => useContext(AuthContext); const Login = () => { let auth = useAuth(); let { signIn } = auth as AuthContextType; const { state } = useLocation() as State; let from = state.from.pathname || "/"; let navigate = useNavigate(); const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); let formData = new FormData(event.currentTarget); let username = formData.get("username") as string; signIn(username, () => navigate(from, { replace: true })); }; return ( <div> <p>您必須登錄才能查看該頁面 {from}</p> <form onSubmit={handleSubmit}> <label> 用戶名: <input name="username" type="text" /> </label> <button type="submit">登錄</button> </form> </div> ); }; export default Login;
開發 Protected 包裹容器
主要就是對登錄狀態進行校驗,成功則渲染子組件,否則跳轉回登錄頁面
import { useContext } from "react"; import { useLocation, Navigate } from "react-router-dom"; import { AuthContext, AuthContextType } from "../AuthProvider"; const useAuth = () => useContext(AuthContext); const RequireAuth = ({ children }: { children: JSX.Element }) => { let auth = useAuth(); let { user } = auth as AuthContextType; let location = useLocation(); if (!user) return <Navigate to="/login" state={{ from: location }} replace />; return children; }; export default RequireAuth;
App 入口文件
入口文件沒有對路由進行懶加載優化,因為是小應用,所以實際開發還是要考慮性能優化的。
import { Routes, Route } from "react-router-dom"; import AuthProvider from "src/views/AuthProvider"; import Layout from "src/views/auth/layout"; import LoginPage from "src/views/auth/login"; import PublicPage from "src/views/auth/publicPage"; import RequireAuth from "src/views/auth/requireAuth"; import ProtectedPage from "src/views/auth/protectedPage"; const App = () => { return ( <AuthProvider> <Routes> <Route element={<Layout />}> <Route path="/" element={<PublicPage />} /> <Route path="/login" element={<LoginPage />} /> <Route path="/protected" element={ <RequireAuth> <ProtectedPage /> </RequireAuth> } /> </Route> </Routes> </AuthProvider> ); }; export default App;
到此這篇關於淺談React-router v6 實現登錄驗證流程的文章就介紹到這瞭,更多相關React-router登錄驗證內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 使用React Router v6 添加身份驗證的方法
- react-router v6實現動態路由實例
- React-Router(V6)的權限控制實現示例
- react-router v6實現權限管理+自動替換頁面標題的案例
- React-Router6版本的更新引起的路由用法變化