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

简介: 使用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处理用户身份验证有了更好的理解。



目录
打赏
0
0
0
0
44
分享
相关文章
除了使用Route组件,React Router还有其他方式处理404错误页面吗
除了使用Route组件,React Router还有其他方式处理404错误页面吗
91 58
如何在React Router中定义404错误页面组件?
如何在React Router中定义404错误页面组件?
87 57
在React Router中,如何处理路由的404错误页面?
在React Router中,如何处理路由的404错误页面?
106 57
如何在React Router中进行嵌套路由配置?
如何在React Router中进行嵌套路由配置?
116 57
如何在React Router中进行路由重定向?
如何在React Router中进行路由重定向?
128 57
如何在React Router中传递参数?
如何在React Router中传递参数?
107 57
React Router 路由管理
【10月更文挑战第10天】本文介绍了 React Router,一个在 React 应用中管理路由的强大工具。内容涵盖基本概念、安装与使用方法、常见问题及解决方案,如路由嵌套、动态路由和路由守卫等,并提供代码示例。通过学习本文,开发者可以更高效地使用 React Router,提升应用的导航体验和安全性。
590 19
实现动态路由与状态管理的SPA——使用React Router与Redux
【10月更文挑战第1天】实现动态路由与状态管理的SPA——使用React Router与Redux
117 1
React Router V6 useRoutes的使用
【8月更文挑战第29天】 React Router V6 useRoutes的使用
357 3
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等