[译]React中的用户认证(登陆态管理)

[译]React中的用户认证(登陆态管理)

原文地址kentcdodds.com/blog/authen…javascript

本文主要展现在当下 React 应用开发中,怎么使用 ContextHooks 来管理用户的认证(也就是登陆态)。java

先说结论

下面是本文最终要实现的的简化版,方便大佬们直接看最后的效果:react

import React from 'react'
import {useUser} from './context/auth'
import AuthenticatedApp from './authenticated-app'
import UnauthenticatedApp from './unauthenticated-app'
function App() {
  const user = useUser()
  return user ? <AuthenticatedApp /> : <UnauthenticatedApp /> } export App 复制代码

嗯,最终的代码大概就长这样。大多数 须要进行用户认证管理的应用,均可以使用相似上面的逻辑来管理用户登陆状态。当用户访问咱们应用中的某个须要登陆后才能访问的页面时,咱们能够将用户重定向到登录页,大多数状况下也是这样作的,除此以外,咱们还能够不进行跳转,直接在该页面上展现未登陆用户看到的界面。为了提升用户体验,咱们也能够这样:git

import React from 'react'
import {useUser} from './context/auth'
const AuthenticatedApp = React.lazy(() => import('./authenticated-app'))
const UnauthenticatedApp = React.lazy(() => import('./unauthenticated-app'))
function App() {
  const user = useUser()
  return user ? <AuthenticatedApp /> : <UnauthenticatedApp /> } export App 复制代码

亲,代码 懒加载 就实现了:未登陆用户访问咱们页面,只会加载渲染未登陆界面的代码;已登陆用户访问同一个页面,一样只会加载渲染已登陆界面的代码。github

具体在 AuthenticatedAppUnauthenticatedApp 里渲染什么界面,彻底是开发者来决定。或许他们会渲染一些 router ,甚至会复用一些公共组件。不管具体渲染什么,咱们都不用关心用户的登陆态了,由于在渲染这两个组件之一的时候,已经明确知道了用户的登陆状态。后端

怎么一步步实现上面的逻辑

若是你想看看最终整个APP的实现,能够到 这个github 查看。服务器

OK,咱们接下来看看怎么一步步来实现上面的逻辑。首先,咱们看下咱们应用的入口代码:app

import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
import AppProviders from './context'
ReactDOM.render(
  <AppProviders> <App /> </AppProviders>,
  document.getElementById('root'),
)
复制代码

下面是 AppProviders组件的代码:dom

import React from 'react'
import {AuthProvider} from './auth-context'
import {UserProvider} from './user-context'
function AppProviders({children}) {
  return (
    <AuthProvider> <UserProvider>{children}</UserProvider> </AuthProvider>
  )
}
export default AppProviders
复制代码

OK,咱们有两个 Provider: 一个是维护应用的认证状态;另外一个是维护当前登陆用户的数据。按照字面意思,AppProvider 负责初始化整个APP的数据(若是在localStorage中存在用户认证后的token,那么咱们能够直接从token中获取一些用户数据)。另外一方面,UserProvider负责将咱们对用户数据的一些修改(好比email地址、履历等)保持和服务器端的同步。ide

完整的auth-context.js 包含一些和本文主题无关的逻辑,所以下面咱们来看下简化版的 auth-context.js

import React from 'react'
import {FullPageSpinner} from '../components/lib'
const AuthContext = React.createContext()
function AuthProvider(props) {
  // 若是咱们还不肯定当前用户是否登陆,好比还在请求后端接口查询登陆状态,
  // 那么咱们就渲染一个全局的加载中,而不是加载真正的页面组件
  if (weAreStillWaitingToGetTheUserData) {
    return <FullPageSpinner />
  }
  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 (
    <AuthContext.Provider value={{data, login, logout, register}} {...props} />
  )
}
const useAuth = () => React.useContext(AuthContext)
export {AuthProvider, useAuth}
// user-context.js 文件里的 `UserProvider` 大概长这样:
// const UserProvider = props => (
//   <UserContext.Provider value={useAuth().data.user} {...props} />
// )
// and the useUser hook is basically this:
// const useUser = () => React.useContext(UserContext)
复制代码

简化咱们应用里的认证管理的关键点是:

负责维护用户登陆态的组件,在获取到当前用户的登陆状态以前,不会渲染页面的主体内容,能够渲染一个加载中的全局loading。只有当从服务器端获取到用户的登陆状态以后,才去渲染页面的主体:已登陆就渲染登陆后的组件;未登陆渲染未登陆的。

结论

在实际开发中,不少APP面临的场景都是不一样的。若是你采用了服务端渲染技术(SSR),那么你极可能不须要一个加载中的loading,由于你在服务端已经明确知道了,当前用户是否已登陆。即便在这个场景下,一样能够将用户的登陆状态提出到全局的 Provider 来管理,这样会加强咱们代码的可维护性。

PS

有些同窗问到了相同的问题:若是咱们的应用,已登陆用户和未登陆用户看到的不少界面都相同(好比twitter),而不是像gmail那样,已登陆用户和未登陆用户看到的彻底不一样,咱们应该怎么来维护用户登陆状态呢?

若是是这种状况,那么代码里不少组件,都会用到 useUser这个hook,为了能在一个公共组件中,根据是否登陆而执行不一样逻辑。由于咱们的公共组件可能值关心用户是否登陆,你甚至能够再封装出一个 useIsAuthenticated hook,返回一个 boolean 值,表示当前用户是否已登陆。多亏了 ContextHooks,要实现这样的逻辑很是的简单。

相关文章
相关标签/搜索