本文主要展示在當(dāng)下React應(yīng)用開發(fā)中,怎么使用Context和Hooks來管理用戶的認(rèn)證(也就是登錄態(tài))瓦呼。
先說結(jié)論
下面是本文最終要實(shí)現(xiàn)的的簡化版划咐,方便大佬們直接看最后的效果:
importReactfrom'react'import{useUser}from'./context/auth'importAuthenticatedAppfrom'./authenticated-app'importUnauthenticatedAppfrom'./unauthenticated-app'functionApp(){constuser = useUser()returnuser ?:}export App復(fù)制代碼
嗯熟史,最終的代碼大概就長這樣。大多數(shù)需要進(jìn)行用戶認(rèn)證管理的應(yīng)用肋殴,都可以使用類似上面的邏輯來管理用戶登錄狀態(tài)囤锉。當(dāng)用戶訪問我們應(yīng)用中的某個(gè)需要登錄后才能訪問的頁面時(shí),我們可以將用戶重定向到登陸頁护锤,大多數(shù)情況下也是這樣做的官地,除此之外,我們還可以不進(jìn)行跳轉(zhuǎn)烙懦,直接在該頁面上展示未登錄用戶看到的界面驱入。為了提高用戶體驗(yàn),我們也可以這樣:
importReactfrom'react'import{useUser}from'./context/auth'constAuthenticatedApp = React.lazy(()=>import('./authenticated-app'))constUnauthenticatedApp = React.lazy(()=>import('./unauthenticated-app'))functionApp(){constuser = useUser()returnuser ?:}export App復(fù)制代碼
親,代碼懶加載就實(shí)現(xiàn)了:未登錄用戶訪問我們頁面亏较,只會(huì)加載渲染未登錄界面的代碼莺褒;已登錄用戶訪問同一個(gè)頁面,同樣只會(huì)加載渲染已登錄界面的代碼雪情。
具體在AuthenticatedApp和UnauthenticatedApp里渲染什么界面遵岩,完全是開發(fā)者來決定⊙餐ǎ或許他們會(huì)渲染一些router尘执,甚至?xí)?fù)用一些公共組件。無論具體渲染什么宴凉,我們都不用關(guān)心用戶的登錄態(tài)了誊锭,因?yàn)樵阡秩具@兩個(gè)組件之一的時(shí)候,已經(jīng)明確知道了用戶的登錄狀態(tài)弥锄。
怎么一步步實(shí)現(xiàn)上面的邏輯
如果你想看看最終整個(gè)APP的實(shí)現(xiàn)丧靡,可以到這個(gè)github查看。
OK籽暇,我們接下來看看怎么一步步來實(shí)現(xiàn)上面的邏輯温治。首先,我們看下我們應(yīng)用的入口代碼:
importReactfrom'react'importReactDOMfrom'react-dom'importAppfrom'./app'importAppProvidersfrom'./context'ReactDOM.render(,document.getElementById('root'),)復(fù)制代碼
下面是AppProviders組件的代碼:
importReactfrom'react'import{AuthProvider}from'./auth-context'import{UserProvider}from'./user-context'functionAppProviders({children}){return({children})}exportdefaultAppProviders復(fù)制代碼
OK戒悠,我們有兩個(gè)Provider: 一個(gè)是維護(hù)應(yīng)用的認(rèn)證狀態(tài)罐盔;另一個(gè)是維護(hù)當(dāng)前登錄用戶的數(shù)據(jù)。按照字面意思救崔,AppProvider負(fù)責(zé)初始化整個(gè)APP的數(shù)據(jù)(如果在localStorage中存在用戶認(rèn)證后的token,那么我們可以直接從token中獲取一些用戶數(shù)據(jù))捏顺。另一方面六孵,UserProvider負(fù)責(zé)將我們對用戶數(shù)據(jù)的一些修改(比如email地址、履歷等)保持和服務(wù)器端的同步幅骄。
完整的auth-context.js包含一些和本文主題無關(guān)的邏輯劫窒,因此下面我們來看下簡化版的auth-context.js:
importReactfrom'react'import{FullPageSpinner}from'../components/lib'constAuthContext = React.createContext()functionAuthProvider(props){// 如果我們還不確定當(dāng)前用戶是否登錄,比如還在請求后端接口查詢登錄狀態(tài)拆座,// 那么我們就渲染一個(gè)全局的加載中主巍,而不是加載真正的頁面組件if(weAreStillWaitingToGetTheUserData) {return? }? const login = () => {} // make a login request? const register = () => {} // register the user? const logout = () => {} // clear the token in localStorage and the user data? // 注意:這里我并沒有使用 `React.useMemo` 來優(yōu)化 provider 的 `value`。? // 因?yàn)檫@是我們應(yīng)用里最頂級(jí)的組件挪凑,很少會(huì)在這個(gè)頂級(jí)組件上觸發(fā) 重新render? return (? ? ? )}const useAuth = () => React.useContext(AuthContext)export {AuthProvider, useAuth}// user-context.js 文件里的 `UserProvider` 大概長這樣:// const UserProvider = props => (//? // )// and the useUser hook is basically this:// const useUser = () => React.useContext(UserContext)復(fù)制代碼
簡化我們應(yīng)用里的認(rèn)證管理的關(guān)鍵點(diǎn)是:
負(fù)責(zé)維護(hù)用戶登錄態(tài)的組件孕索,在獲取到當(dāng)前用戶的登錄狀態(tài)之前,不會(huì)渲染頁面的主體內(nèi)容躏碳,可以渲染一個(gè)加載中的全局loading搞旭。只有當(dāng)從服務(wù)器端獲取到用戶的登錄狀態(tài)之后,才去渲染頁面的主體:已登錄就渲染登錄后的組件;未登錄渲染未登錄的肄渗。
結(jié)論
在實(shí)際開發(fā)中镇眷,很多APP面臨的場景都是不同的。如果你采用了服務(wù)端渲染技術(shù)(SSR)翎嫡,那么你很可能不需要一個(gè)加載中的loading欠动,因?yàn)槟阍诜?wù)端已經(jīng)明確知道了,當(dāng)前用戶是否已登錄惑申。即使在這個(gè)場景下具伍,同樣可以將用戶的登錄狀態(tài)提出到全局的Provider來管理,這樣會(huì)增強(qiáng)我們代碼的可維護(hù)性硝桩。
PS:
有些同學(xué)問到了相同的問題:如果我們的應(yīng)用沿猜,已登錄用戶和未登錄用戶看到的很多界面都相同(比如twitter),而不是像gmail那樣碗脊,已登錄用戶和未登錄用戶看到的完全不同啼肩,我們應(yīng)該怎么來維護(hù)用戶登錄狀態(tài)呢?
如果是這種情況,那么代碼里很多組件衙伶,都會(huì)用到useUser這個(gè)hook祈坠,為了能在一個(gè)公共組件中,根據(jù)是否登錄而執(zhí)行不同邏輯矢劲。因?yàn)槲覀兊墓步M件可能值關(guān)心用戶是否登錄赦拘,你甚至可以再封裝出一個(gè)useIsAuthenticatedhook,返回一個(gè)boolean值芬沉,表示當(dāng)前用戶是否已登錄躺同。多虧了Context和Hooks,要實(shí)現(xiàn)這樣的邏輯非常的簡單丸逸。
我自己是一個(gè)從事了6年的Java全棧工程師蹋艺,最近整理了一套適合2019年學(xué)習(xí)的Java\大數(shù)據(jù)資料,從基礎(chǔ)的Java黄刚、大數(shù)據(jù)面向?qū)ο蟮竭M(jìn)階的框架知識(shí)都有整理哦捎谨,可以來我的主頁免費(fèi)領(lǐng)取哦。