使用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.jsx
和Home.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 API
和useContext
钩子处理通过身份验证的用户的状态。
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 Router
的 useNavigate
钩子将他们重定向到主页。
为了在页面刷新时保持用户的状态,我们将使用 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