淺談React-router v6 實(shí)現(xiàn)登錄驗(yàn)證流程
此示例演示了一個(gè)包含三個(gè)頁(yè)面的簡(jiǎn)單登錄流程:公共頁(yè)面、受保護(hù)頁(yè)面和登錄頁(yè)面。 為了查看受保護(hù)的頁(yè)面,你必須先登錄。
首先,訪問公共頁(yè)面。 然后,訪問受保護(hù)的頁(yè)面。 你尚未登錄,因此你將被重定向到登錄頁(yè)面。 登錄后,你將被重定向回受保護(hù)的頁(yè)面。
封裝 Context 包裹容器
首先封裝AuthProvider組件,利用Context特性共享那些對(duì)于一個(gè)組件樹而言是“全局”的數(shù)據(jù)。
全局定義user、signIn、signOut數(shù)據(jù)和方法,signIn、signOut使用了高階函數(shù),也方便后續(xù)擴(kuò)展和修改。
Context主要應(yīng)用場(chǎng)景在于很多不同層級(jí)的組件需要訪問同樣一些的數(shù)據(jù)。請(qǐng)謹(jǐn)慎使用,因?yàn)檫@會(huì)使得組件的復(fù)用性變差。
如果你只是想避免層層傳遞一些屬性,組件組合(component composition)有時(shí)候是一個(gè)比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 父級(jí)容器
Layout組件主要是針對(duì)登錄狀態(tài)進(jìn)行校驗(yàn),然后做相應(yīng)處理。利用react-router v6中<Outlet />組件顯示嵌套路由,相比于v5版本v6實(shí)現(xiàn)嵌套路由更加方便,省略了很多冗余的判斷代碼。
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="/">公共頁(yè)面</Link>
</li>
<li>
<Link to="/protected">受保護(hù)頁(yè)面</Link>
</li>
</ul>
<Outlet />
</div>
);
};
export default Layout;開發(fā) 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>您必須登錄才能查看該頁(yè)面 {from}</p>
<form onSubmit={handleSubmit}>
<label>
用戶名: <input name="username" type="text" />
</label>
<button type="submit">登錄</button>
</form>
</div>
);
};
export default Login;開發(fā) Protected 包裹容器
主要就是對(duì)登錄狀態(tài)進(jìn)行校驗(yàn),成功則渲染子組件,否則跳轉(zhuǎn)回登錄頁(yè)面
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 入口文件
入口文件沒有對(duì)路由進(jìn)行懶加載優(yōu)化,因?yàn)槭切?yīng)用,所以實(shí)際開發(fā)還是要考慮性能優(yōu)化的。
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;
到此這篇關(guān)于淺談React-router v6 實(shí)現(xiàn)登錄驗(yàn)證流程的文章就介紹到這了,更多相關(guān)React-router登錄驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決React報(bào)錯(cuò)Unexpected default export of an
這篇文章主要為大家介紹了React報(bào)錯(cuò)Unexpected default export of anonymous function解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
React利用lazy+Suspense實(shí)現(xiàn)路由懶加載
這篇文章主要為大家詳細(xì)介紹了React如何利用lazy+Suspense實(shí)現(xiàn)路由懶加載,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下2023-06-06
詳解React中多種組件通信方式的實(shí)現(xiàn)
在React中,組件之間的通信是一個(gè)非常重要的話題,React提供了幾種方式來實(shí)現(xiàn)跨組件通信,下面小編將詳細(xì)講講其中幾種通信方式,并提供實(shí)際的代碼示例,需要的可以參考下2023-11-11
React Native使用百度Echarts顯示圖表的示例代碼
本篇文章主要介紹了React Native使用百度Echarts顯示圖表的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
react使用css module無法重寫bootstrap樣式問題及解決
這篇文章主要介紹了react使用css module無法重寫bootstrap樣式問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
react?Scheduler?實(shí)現(xiàn)示例教程
這篇文章主要為大家介紹了react?Scheduler?實(shí)現(xiàn)示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
從零開始搭建一個(gè)react項(xiàng)目開發(fā)
這篇文章主要介紹了從零開始搭建一個(gè)react項(xiàng)目開發(fā),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02
React中Portals與錯(cuò)誤邊界處理實(shí)現(xiàn)
本文主要介紹了React中Portals與錯(cuò)誤邊界處理實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07

