本文主要展示在当下React应用开发中,怎么使用Context和Hooks来管理用户的认证(也就是登录态)。
先说结论
下面是本文最终要实现的的简化版,方便大佬们直接看最后的效果:
importReactfrom'react'import{useUser}from'./context/auth'importAuthenticatedAppfrom'./authenticated-app'importUnauthenticatedAppfrom'./unauthenticated-app'functionApp(){constuser = useUser()returnuser ?:}export App复制代码
嗯,最终的代码大概就长这样。大多数需要进行用户认证管理的应用,都可以使用类似上面的逻辑来管理用户登录状态。当用户访问我们应用中的某个需要登录后才能访问的页面时,我们可以将用户重定向到登陆页,大多数情况下也是这样做的,除此之外,我们还可以不进行跳转,直接在该页面上展示未登录用户看到的界面。为了提高用户体验,我们也可以这样:
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复制代码
亲,代码懒加载就实现了:未登录用户访问我们页面,只会加载渲染未登录界面的代码;已登录用户访问同一个页面,同样只会加载渲染已登录界面的代码。
具体在AuthenticatedApp和UnauthenticatedApp里渲染什么界面,完全是开发者来决定。或许他们会渲染一些router,甚至会复用一些公共组件。无论具体渲染什么,我们都不用关心用户的登录态了,因为在渲染这两个组件之一的时候,已经明确知道了用户的登录状态。
怎么一步步实现上面的逻辑
如果你想看看最终整个APP的实现,可以到这个github查看。
OK,我们接下来看看怎么一步步来实现上面的逻辑。首先,我们看下我们应用的入口代码:
importReactfrom'react'importReactDOMfrom'react-dom'importAppfrom'./app'importAppProvidersfrom'./context'ReactDOM.render(,document.getElementById('root'),)复制代码
下面是AppProviders组件的代码:
importReactfrom'react'import{AuthProvider}from'./auth-context'import{UserProvider}from'./user-context'functionAppProviders({children}){return({children})}exportdefaultAppProviders复制代码
OK,我们有两个Provider: 一个是维护应用的认证状态;另一个是维护当前登录用户的数据。按照字面意思,AppProvider负责初始化整个APP的数据(如果在localStorage中存在用户认证后的token,那么我们可以直接从token中获取一些用户数据)。另一方面,UserProvider负责将我们对用户数据的一些修改(比如email地址、履历等)保持和服务器端的同步。
完整的auth-context.js包含一些和本文主题无关的逻辑,因此下面我们来看下简化版的auth-context.js:
importReactfrom'react'import{FullPageSpinner}from'../components/lib'constAuthContext = React.createContext()functionAuthProvider(props){// 如果我们还不确定当前用户是否登录,比如还在请求后端接口查询登录状态,// 那么我们就渲染一个全局的加载中,而不是加载真正的页面组件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` 来优化 provider 的 `value`。 // 因为这是我们应用里最顶级的组件,很少会在这个顶级组件上触发 重新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)复制代码
简化我们应用里的认证管理的关键点是:
负责维护用户登录态的组件,在获取到当前用户的登录状态之前,不会渲染页面的主体内容,可以渲染一个加载中的全局loading。只有当从服务器端获取到用户的登录状态之后,才去渲染页面的主体:已登录就渲染登录后的组件;未登录渲染未登录的。
结论
在实际开发中,很多APP面临的场景都是不同的。如果你采用了服务端渲染技术(SSR),那么你很可能不需要一个加载中的loading,因为你在服务端已经明确知道了,当前用户是否已登录。即使在这个场景下,同样可以将用户的登录状态提出到全局的Provider来管理,这样会增强我们代码的可维护性。
PS:
有些同学问到了相同的问题:如果我们的应用,已登录用户和未登录用户看到的很多界面都相同(比如twitter),而不是像gmail那样,已登录用户和未登录用户看到的完全不同,我们应该怎么来维护用户登录状态呢?
如果是这种情况,那么代码里很多组件,都会用到useUser这个hook,为了能在一个公共组件中,根据是否登录而执行不同逻辑。因为我们的公共组件可能值关心用户是否登录,你甚至可以再封装出一个useIsAuthenticatedhook,返回一个boolean值,表示当前用户是否已登录。多亏了Context和Hooks,要实现这样的逻辑非常的简单。
我自己是一个从事了6年的Java全栈工程师,最近整理了一套适合2019年学习的Java\大数据资料,从基础的Java、大数据面向对象到进阶的框架知识都有整理哦,可以来我的主页免费领取哦。
网友评论