使用React Router v6 进行身份验证完全指南

Posted 前端修罗场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用React Router v6 进行身份验证完全指南相关的知识,希望对你有一定的参考价值。

React Router v6是React应用程序的一个流行且功能强大的路由库。它提供了一种声明式的、基于组件的路由方法,并能处理URL参数、重定向和加载数据等常见任务

这个最新版本的React Router引入了很多新概念,比如<Outlet />layout布局路由,但相关文档仍然很少。

本文将演示如何使用React Router v6创建受保护的路由以及如何添加身份验证。

开始

打开终端,运行以下命令创建一个新的 React 项目:

> npx create-react-app ReactRouterAuthDemo
> cd ReactRouterAuthDemo

接下来,在 React 应用程序中安装 React Router 作为依赖项:

> npm install react-router-dom

一旦 React Router 依赖项安装好,我们就可以开始编辑src/index.js文件。

首先,从 react-router-dom 中导入 BrowserRouter组件,然后用<BrowserRouter /> 包裹 <App /> 组件,就像这样:

import  StrictMode  from "react";
import  createRoot  from "react-dom/client";
import  BrowserRouter  from "react-router-dom";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>
);

基础路由

React Router提供了 <Routes /><Route /> 组件,使我们能够根据组件的当前位置来渲染它们。

import  Routes, Route  from "react-router-dom";
import  LoginPage  from "./pages/Login";
import  HomePage  from "./pages/Home";
import "./styles.css";

export default function App() 
  return (
    <Routes>
      <Route path="/" element=<HomePage /> />
      <Route path="/login" element=<LoginPage /> />
    </Routes>
  );

<Route />提供了应用程序和 React 组件之间路径的映射。例如,当用户导航到/login时,要渲染LoginPage组件,我们只需要像这样提供<Route />:

<Route path="/login" element=<LoginPage /> />

<Route /> 组件可以看作是一个 if 语句,只有当元素与指定的路径匹配时,它才会作用于URL的位置。

<Routes /> 组件是 React Router v5 中的 <Switch /> 组件的替代品。

我们可以通过创建Login.jsxHome.jsx来使用 <Routes />

// Login.jsx
export const LoginPage = () => (
  <div>
    <h1>This is the Login Page</h1>
  </div>
);

// Home.jsx
export const HomePage = () => (
  <div>
    <h1>This is the Home Page</h1>
  </div>
);

接下来,我们将运行下面的命令来启动应用程序:

> npm run start

在浏览器中,我们默认会看到Home组件。如果我们使用/login路由,我们将看到LoginPage组件呈现在屏幕上。

或者,我们也可以使用一个普通的javascript对象,通过useRoutes钩子来表示应用程序中的路由。这是一种定义路由的功能方法,其工作方式与< routes /><Route />组件相同。

import  useRoutes  from "react-router-dom";
// ...

export default function App() 
  const routes = useRoutes([
    
      path: "/",
      element: <HomePage />
    ,
    
      path: "/login",
      element: <LoginPage />
    
  ]);
  return routes;

既然基本设置已经完成,让我们看看如何创建受保护的路由,从而使未经身份验证的用户无法访问应用程序中的某些内容。

创建受保护的路由

在创建受保护的路由之前,让我们先创建一个自定义钩子,它将使用Context APIuseContext钩子处理通过身份验证的用户的状态。

import  createContext, useContext, useMemo  from "react";
import  useNavigate  from "react-router-dom";
import  useLocalStorage  from "./useLocalStorage";
const AuthContext = createContext();

export const AuthProvider = ( children ) => 
  const [user, setUser] = useLocalStorage("user", null);
  const navigate = useNavigate();

  // 验证用户权限的时候,访问该函数
  const login = async (data) => 
    setUser(data);
    navigate("/profile");
  ;

  // 登出
  const logout = () => 
    setUser(null);
    navigate("/",  replace: true );
  ;

  const value = useMemo(
    () => (
      user,
      login,
      logout
    ),
    [user]
  );
  return <AuthContext.Provider value=value>children</AuthContext.Provider>;
;

export const useAuth = () => 
  return useContext(AuthContext);
;

上述 useAuth 钩子中,我们暴露了用户的状态和一些用于用户登录和注销的方法。当用户登出时,我们使用 React RouteruseNavigate 钩子将他们重定向到主页。

为了在页面刷新时保持用户的状态,我们将使用 useLocalStorage 钩子,它将在浏览器的本地存储中同步状态值。

import  useState  from "react";

export const useLocalStorage = (keyName, defaultValue) => 
  const [storedValue, setStoredValue] = useState(() => 
    try 
      const value = window.localStorage.getItem(keyName);
      if (value) 
        return JSON.parse(value);
       else 
        window.localStorage.setItem(keyName, JSON.stringify(defaultValue));
        return defaultValue;
      
     catch (err) 
      return defaultValue;
    
  );
  const setValue = (newValue) => 
    try 
      window.localStorage.setItem(keyName, JSON.stringify(newValue));
     catch (err) 
    setStoredValue(newValue);
  ;
  return [storedValue, setValue];
;

<ProtectedRoute /> 组件将从 useAuth 钩子中检查当前用户的状态,如果用户没有经过身份验证,则重定向到/路径。

import  Navigate  from "react-router-dom";
import  useAuth  from "../hooks/useAuth";

export const ProtectedRoute = ( children ) => 
  const  user  = useAuth();
  if (!user) 
    // user is not authenticated
    return <Navigate to="/" />;
  
  return children;
;

要重定向用户,我们使用 <Navigate /> 组件。当父组件呈现当前位置时,<Navigate /> 组件会改变当前位置。它在内部使用 usenavate 钩子。

App.js 文件中,我们可以用 <ProtectedRoute /> 组件包装page 组件。例如下面,我们使用 <ProtectedRoute /> 包装<SettingsPage /><ProfilePage /> 组件。现在,当未经身份验证的用户试图访问 /profile/settings 路径时,他们将被重定向到主页。

import  Routes, Route  from "react-router-dom";
import  LoginPage  from "./pages/Login";
import  HomePage  from "./pages/Home";
import  SignUpPage  from "./pages/SignUp";
import  ProfilePage  from "./pages/Profile";
import  SettingsPage  from "./pages/Settings";
import  ProtectedRoute  from "./components/ProtectedRoute";


export default function App() 
  return (
    <Routes>
      <Route path="/" element=<HomePage /> />
      <Route path="/login" element=<LoginPage /> />
      <Route path="/register" element=<SignUpPage /> />
      <Route
        path="/profile"
        element=
          <ProtectedRoute>
            <ProfilePage />
          </ProtectedRoute>
        
      />
      <Route
        path="/settings"
        element=
          <ProtectedRoute>
            <SettingsPage />
          </ProtectedRoute>
        
      />
    </Routes>
  );

如果受保护的路由数量有限,上面的方法工作得很好,但如果有多个这样的路由,我们就必须把每个都包装起来,这很繁琐。

相反,我们可以使用React Router v6的嵌套路由特性将所有受保护的路由封装在一个布局中。

使用嵌套路由和< Outlet />

React Router v6中最强大的特性之一是嵌套路由。这个特性允许我们有一个包含其他子路由的路由。我们的大多数布局都与URL上的片段相耦合,React Router完全支持这一点。

例如,我们可以在<HomePage /><LoginPage /> 路由中添加一个父组件 <Route />,就像这样:

import  ProtectedLayout  from "./components/ProtectedLayout";
import  HomeLayout  from "./components/HomeLayout";
// ...

export default function App() 
  return (
    <Routes>
      <Route element=<HomeLayout />>
        <Route path="/" element=<HomePage /> />
        <Route path="/login" element=<LoginPage /> />
      </Route>

      <Route path="/dashboard" element=<ProtectedLayout />>
        <Route path="profile" element=<ProfilePage /> />
        <Route path="settings" element=<SettingsPage /> />
      </Route>
    </Routes>
  );

父组件 <Route /> 也可以有一个路径,它负责在屏幕上呈现子组件<Route />

当用户导航到 /dashboard/profile 时,路由器将呈现 <ProfilePage />。为了实现这一点,父路由元素必须有一个 <Outlet /> 组件来呈现子元素。Outlet 组件使嵌套的 UI 在呈现子路由时可见。

父路由元素还可以具有额外的公共业务逻辑和用户界面。例如,在<ProtectedLayout /> 组件中,我们已经包含了私有路由逻辑和一个通用导航条,当子路由被呈现时,它将是可见的。

import  Navigate, Outlet  from "react-router-dom";
import  useAuth  from "../hooks/useAuth";

export const ProtectedLayout = () => 
  const  user  = useAuth();

  if (!user) 
    return <Navigate to="/" />;
  

  return (
    <div>
      <nav>
        <Link to="/settings">Settings</Link>
        <Link to="/profile">Profile</Link>
      </nav>
      <Outlet />
    </div>
  )
;

除了<Outlet />组件,我们还可以选择使用 useOutlet 钩子,它的作用是一样的:

import  Link, Navigate, useOutlet  from "react-router-dom";
// ...

export const ProtectedLayout = () => 
  const  user  = useAuth();
  const outlet = useOutlet();

  if (!user) 
    return <Navigate to="/" />;
  

  return (
    <div>
      <nav>
        <Link to="/settings">Settings</Link>
        <Link to="/profile">Profile</Link>
      </nav>
      outlet
    </div>
  );
;

与受保护路由类似,我们不希望通过身份验证的用户访问 /login 路径。让我们在 <HomeLayout /> 组件中处理它:

import  Navigate, Outlet  from "react-router-dom";
import  useAuth  from "../hooks/useAuth";

export const HomeLayout = () => 
  const  user  = useAuth();

  if (user) 
    return <Navigate to="/dashboard/profile" />;
  

  return (
    <div>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/login">Login</Link>
      </nav>
      <Outlet />
    </div>
  )
;

结尾

值得花一些时间来更好地理解 React Router v6 的工作原理,特别是用户身份验证。

与以前的版本相比,React Router v6是一个巨大的改进。它快速、稳定、可靠。除了更容易使用之外,它还有很多新特性,比如<Outlets />和一个改进的<Route />组件,这大大简化了 React 应用中的路由。

我希望本指南对您有所帮助,希望您对如何使用React Router v6处理用户身份验证有了更好的理解。

以上是关于使用React Router v6 进行身份验证完全指南的主要内容,如果未能解决你的问题,请参考以下文章

使用 Passport + Facebook + Express + create-react-app + React-Router + 代理进行身份验证

如何在使用 Passport 进行社交身份验证后重定向到正确的客户端路由(react、react-router、express、passport)

如何使用 rnfirebase 动态链接和身份验证模块 v6 在 React Native 中实现 signInWithEmailLink

React Router - 身份验证后重定向延迟

使用 react-router-v4 对路由执行身份验证

从cookie异步加载身份验证令牌时,React react-router-dom私有路由不起作用