”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 Nanostores 和 Context API 在 React 应用程序中处理身份验证

使用 Nanostores 和 Context API 在 React 应用程序中处理身份验证

发布于2024-08-20
浏览:398

HANDLING AUTH IN REACT APPS USING NANOSTORES AND CONTEXT API

在我开始使用 ReactJs 构建全栈 Web 应用程序时,我发现自己对如何在前端处理身份验证感到困惑。我的意思是,从后端收到访问令牌后,您下一步应该做什么?如何保留登录状态?

大多数初学者会假设“哦,只需将您的令牌存储在状态中即可”。但我很快发现这不是最好的解决方案,甚至根本不是一个解决方案,因为正如大多数经验丰富的 ReactJs 开发人员所知,状态是暂时的,因为每次刷新页面时它都会被清除,我们绝对可以'用户每次刷新都不需要登录。

快进到现在,我已经获得了一些在 React 中构建全栈应用程序的经验,研究了更有经验的开发人员的身份验证方法并在其他两个应用程序中复制了该过程,我想提供一个指南关于我目前如何处理它。有些人可能认为这不是最好的方法,但我现在已经采用它作为我的方法,并且我愿意学习其他开发人员使用的其他方法。

第一步

您已将您的电子邮件和密码(假设您使用基本电子邮件和密码身份验证)提交到后端以启动身份验证过程。我不会谈论后端如何处理身份验证,因为本文是关于如何仅在前端处理身份验证。我将跳到您在 HTTP 响应中收到令牌的部分。下面是一个简单登录表单组件的代码示例,该组件将电子邮件和密码提交到服务器并在响应中接收令牌和用户信息。现在为了简单起见,我的表单值是通过状态来管理的,对于生产应用程序来说,使用像 formik 这样强大的库会更好。

import axios from 'axios'
import { useState } from "react"

export default function LoginForm() {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")

    const handleSubmit = async() => {
        try {
            const response = await axios.post("/api/auth/login", { email, password })
            if (response?.status !== 200) {
                throw new Error("Failed login")
            }
            const token = response?.data?.token
            const userInfo = response?.data?.userInfo
        } catch (error) {
            throw error
        }
    }

    return(
        
setEmail(e.target.value)}/> setPassword(e.target.value)}/>
) }

第二步

包装整个应用程序,或仅包装需要访问身份验证上下文提供程序中的身份验证状态的部分。这通常在根 App.jsx 文件中完成。如果您不知道 context API 是什么,请随时查看 Reactjs 文档。下面的示例显示了创建的 AuthContext 提供程序组件。然后将其导入到 App.jsx 中并用于包装 App 组件中返回的 RouterProvider,从而使身份验证状态可以从应用程序中的任何位置访问。

import { createContext } from "react";

export const AuthContext = createContext(null)

export default function AuthProvider({children}) {

    return(
        
            {children}
        
    )
}
import React from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import AuthProvider from "./AuthContext";

const router = createBrowserRouter([
    // your routes here
])

function App() {
    return(
        
    )
}

export default App

第三步

在身份验证上下文中,您必须初始化两个状态变量“isLoggedIn”和“authenticatedUser”。第一个状态是布尔类型,最初设置为“false”,然后在确认登录后更新为“true”。第二个状态变量用于存储登录用户的信息,例如姓名、电子邮件等。这些状态变量必须包含在上下文组件中返回的提供程序的值中,以便可以在整个应用程序中访问它们以进行条件渲染.

import { createContext, useState } from "react";

export const AuthContext = createContext(null)

export default function AuthProvider({children}) {
    const [isLoggedIn, setIsLoggedIn] = useState(false)
    const [authenticatedUser, setAuthenticatedUser] = useState(null)

    const values = {
        isLoggedIn,
        authenticatedUser,
        setAuthenticatedUser
    }

    return(
        
            {children}
        
    )
}

第四步

Nanostores 是一个用于管理 Javascript 应用程序状态的包。该包提供了一个简单的 API,用于管理跨多个组件的状态值,只需在单独的文件中初始化它并将其导入到您想要使用状态或更新状态的任何组件中即可。但是,为了存储在第一步中的 HTTP 响应中收到的身份验证令牌,您将使用 nanostores/persistent。该包通过将状态存储在 localStorage 中来保留您的状态,这样当您刷新页面时它就不会被清除。 @nanostores/react 是针对 Nanostore 的 React 特定集成,它使 useStore 钩子可用于从 nanostore 状态中提取值。

所以现在您可以继续:

  • 安装以下软件包:nanostores、@nanostores/persistent 和 @nanostores/react。

  • 在名为 user.atom.js 或您选择的任何名称的单独文件中,使用 nanostores/persistent 初始化“authToken”存储和“user”存储。

  • 将它们导入到您的登录表单组件文件中,并使用登录响应中收到的令牌和用户数据更新状态。

npm i nanostores @nanostores/persistent @nanostores/react
import { persistentMap } from '@nanostores/persistent'

export const authToken = persistentMap('token', null)

export const user = persistentMap('user', null)
import { authToken, user } from './user.atom'

 const handleSubmit = async() => {
        try {
            const response = await axios.post("/api/auth/login", { email, password })
            if (response?.status !== 200) {
                throw new Error("Failed login")
            }
            const token = response?.data?.token
            const userInfo = response?.data?.userInfo

            authToken.set(token)
            user.set(userInfo)
        } catch (error) {
            throw error
        }
    }

第五步

现在,在包装应用程序的身份验证上下文中,您必须确保令牌和用户状态保持更新并在整个应用程序中可用。要实现这一目标,您必须:

  • 导入“authToken”和“user”存储。

  • 初始化一个 useEffect 钩子,在钩子内部创建一个 'checkLogin()' 函数,该函数将检查令牌是否存在于 'authToken' 存储中,如果存在,则运行一个函数来检查它是否存在已到期。根据检查结果,您可以将用户重定向到登录页面以进行身份​​验证,或者...将“isLoggedIn”状态设置为 true。现在,为了确保更频繁地跟踪登录状态,可以将此挂钩设置为在每次当前路径更改时运行,这样,如果用户的令牌在与应用程序交互时过期,则用户可能会被踢出或重定向到登录页面。

  • 初始化另一个 useEffect 钩子,该钩子将包含一个函数,用于在每次加载或刷新应用程序时使用 authToken 存储中的令牌从后端获取用户信息。如果您收到成功的响应,请将“isLoggedIn”状态设置为 true,并使用响应中收到的用户信息更新“authenticatedUser”状态和“user”存储。

下面是更新后的AuthProvider组件文件。

import { createContext, useState } from "react";
import { authToken, user } from './user.atom';
import { useStore } from "@nanostores/react";
import { useNavigate, useLocation } from "react-router-dom";
import axios from "axios";

export const AuthContext = createContext(null)

export default function AuthProvider({children}) {
    const [isLoggedIn, setIsLoggedIn] = useState(false)
    const [authenticatedUser, setAuthenticatedUser] = useState(null)
    const token = useStore(authToken)
    const navigate = useNavigate()
    const { pathname } = useLocation()

    function isTokenExpired() {
        // verify token expiration and return true or false
    }

    // Hook to check if user is logged in 
    useEffect(() => {
        async function checkLogin () {
            if (token) {

              const expiredToken = isTokenExpired(token);

              if (expiredToken) {
                // clear out expired token and user from store and navigate to login page
                authToken.set(null)
                user.set(null)
                setIsLoggedIn(false);
                navigate("/login");
                return;
              }
            }
        };

        checkLogin()
    }, [pathname])

    // Hook to fetch current user info and update state
    useEffect(() => {
        async function fetchUser() {
            const response = await axios.get("/api/auth/user", {
                headers: {
                    'Authorization': `Bearer ${token}`
                }
            })

            if(response?.status !== 200) {
                throw new Error("Failed to fetch user data")
            }

            setAuthenticatedUser(response?.data)
            setIsLoggedIn(true)
        }

        fetchUser()
    }, [])

    const values = {
        isLoggedIn,
        authenticatedUser,
        setAuthenticatedUser
    }

    return(
        
            {children}
        
    )
}

结论

现在,第五步中创建的这两个 useEffect 挂钩负责管理整个应用程序的身份验证状态。每次您进行刷新时,它们都会运行以检查本地存储中的令牌,直接从后端检索最新的用户数据并更新您的“isLoggedIn”和“authenticatedUser”状态。您可以通过从 React 导入“AuthContext”和“useContext”钩子并在组件中调用它们来访问值并将它们用于某些条件渲染,从而在任何组件中使用状态。

import { useContext } from "react";
import { AuthContext } from "./AuthContext";

export default function MyLoggedInComponent() {

    const { isLoggedIn, authenticatedUser } = useContext(AuthContext)

    return(
        
        {
            isLoggedIn ? 
            

Welcome {authenticatedUser?.name}

: } > ) }

请记住,注销时,您必须通过将“authToken”和“user”存储设置为空来清除它们。您还需要将“isLoggedIn”设置为 false,将“authenticatedUser”设置为 null。

感谢您的阅读!

版本声明 本文转载于:https://dev.to/ikemcodedit/handling-auth-in-react-apps-using-nanostores-and-context-api-35kg?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • PART# 使用 HTTP 进行大型数据集的高效文件传输系统
    PART# 使用 HTTP 进行大型数据集的高效文件传输系统
    让我们分解提供的HTML、PHP、JavaScript和CSS代码对于分块文件上传仪表板部分。 HTML 代码: 结构概述: Bootstrap for Layout:代码使用 Bootstrap 4.5.2 创建一个包含两个主要部分的响应式布局: 分块上传部分:用于...
    编程 发布于2024-11-06
  • 比较:Lithe 与其他 PHP 框架
    比较:Lithe 与其他 PHP 框架
    如果您正在为下一个项目探索 PHP 框架,很自然会遇到 Laravel、Symfony 和 Slim 等选项。但是,是什么让 Lithe 与这些更强大、更知名的框架区分开来呢?以下是一些突出 Lithe 如何脱颖而出的注意事项。 1. 轻量级和性能 Lithe 的设计重点关注轻量级架...
    编程 发布于2024-11-06
  • 编码风格指南:编写简洁代码的实用指南
    编码风格指南:编写简洁代码的实用指南
    在过去的五年里,我一直在不断尝试提高我的编码技能,其中之一就是学习和遵循最推荐的编码风格。 本指南旨在帮助您编写一致且优雅的代码,并包含一些提高代码可读性和可维护性的建议。它的灵感来自于社区中最受接受的流行指南,但进行了一些修改以更适合我的喜好。 值得一提的是,我是一名全栈 JavaScript 开...
    编程 发布于2024-11-06
  • 检查类型是否满足 Go 中的接口
    检查类型是否满足 Go 中的接口
    在Go中,开发人员经常使用接口来定义预期的行为,使代码灵活且健壮。但是如何确保类型真正实现接口,尤其是在大型代码库中? Go 提供了一种简单有效的方法来在编译时验证这一点,防止运行时错误的风险并使您的代码更加可靠和可读。 您可能见过类似的语法 var _ InterfaceName = TypeN...
    编程 发布于2024-11-06
  • 掌握 JavaScript 中的 &#this&# 关键字
    掌握 JavaScript 中的 &#this&# 关键字
    JavaScript 中的 this 关键字如果不理解的话可能会非常棘手。这是即使是经验丰富的开发人员也很难轻松掌握的事情之一,但一旦你掌握了,它可以为你节省大量时间。 在本文中,我们将了解它是什么、它在不同情况下如何工作以及使用它时不应陷入的常见错误。 在 JavaScript ...
    编程 发布于2024-11-06
  • PHP 中的用户浏览器检测可靠吗?
    PHP 中的用户浏览器检测可靠吗?
    使用 PHP 进行可靠的用户浏览器检测确定用户的浏览器对于定制 Web 体验至关重要。 PHP 提供了两种可能的方法: $_SERVER['HTTP_USER_AGENT'] 和 get_browser() 函数。$_SERVER['HTTP_USER_AGENT']...
    编程 发布于2024-11-06
  • 增强您的 Web 动画:像专业人士一样优化 requestAnimationFrame
    增强您的 Web 动画:像专业人士一样优化 requestAnimationFrame
    流畅且高性能的动画在现代 Web 应用程序中至关重要。然而,管理不当可能会使浏览器的主线程过载,导致性能不佳和动画卡顿。 requestAnimationFrame (rAF) 是一种浏览器 API,旨在将动画与显示器的刷新率同步,从而确保与 setTimeout 等替代方案相比更流畅的运动。但有效...
    编程 发布于2024-11-06
  • 为什么MySQL服务器在60秒内就消失了?
    为什么MySQL服务器在60秒内就消失了?
    MySQL 服务器已消失 - 恰好在 60 秒内在此场景中,之前成功运行的 MySQL 查询现在遇到了60 秒后超时,显示错误“MySQL 服务器已消失”。即使调整了 wait_timeout 变量,问题仍然存在。分析:超时正好发生在 60 秒,这表明是设置而不是资源限制是原因。直接从 MySQL ...
    编程 发布于2024-11-06
  • 为什么带有“display: block”和“width: auto”的按钮无法拉伸以填充其容器?
    为什么带有“display: block”和“width: auto”的按钮无法拉伸以填充其容器?
    了解具有“display: block”和“width: auto”的按钮的行为当您设置“display: block”时一个按钮,它会调整其布局以占据可用的整个宽度。但是,如果将其与“width: auto”结合使用,则按钮会出现意外行为,并且无法拉伸以填充其容器。此行为源于按钮作为替换元素的基本...
    编程 发布于2024-11-06
  • 为 Bluesky Social 创建机器人
    为 Bluesky Social 创建机器人
    How the bot will work We will develop a bot for the social network Bluesky, we will use Golang for this, this bot will monitor some hashtags ...
    编程 发布于2024-11-06
  • 为什么 PHP 的浮点运算会产生意外的结果?
    为什么 PHP 的浮点运算会产生意外的结果?
    PHP 中的浮点数计算精度:为什么它很棘手以及如何克服它在 PHP 中处理浮点数时,这一点至关重要了解其固有的准确性限制。如代码片段所示:echo("success");} else {echo("error");} 您可能会惊讶地发现,尽管值之间的差异小于 ...
    编程 发布于2024-11-06
  • Python中可以通过变量ID逆向获取对象吗?
    Python中可以通过变量ID逆向获取对象吗?
    从 Python 中的变量 ID 检索对象引用Python 中的 id() 函数返回对象的唯一标识。人们很容易想知道是否可以反转此过程并从其 ID 获取对象。具体来说,我们想要检查取消引用变量的 ID 是否会检索原始对象:dereference(id(a)) == a理解解引用的概念及其在 Pyth...
    编程 发布于2024-11-06
  • Go 的 Defer 关键字如何在函数执行顺序中发挥作用?
    Go 的 Defer 关键字如何在函数执行顺序中发挥作用?
    了解 Go 的 Defer 关键字的功能使用 Go 时,了解 defer 关键字的行为至关重要。该关键字允许开发人员推迟函数的执行,直到周围的函数返回。但是,需要注意的是,函数的值和参数在执行 defer 语句时进行评估。示例:评估 Defer Order为了说明这一点,请考虑以下内容代码:pack...
    编程 发布于2024-11-06
  • WordPress Gutenberg 全局状态管理初学者指南
    WordPress Gutenberg 全局状态管理初学者指南
    构建复杂的 WordPress 块编辑器 (Gutenberg) 应用程序时,有效管理状态变得至关重要。这就是 @wordpress/data 发挥作用的地方。它允许您跨 WordPress 应用程序中的不同块和组件管理和共享全局状态。 如果您不熟悉管理全局状态或使用@wordpress/data,...
    编程 发布于2024-11-06
  • 亚马逊解析简单且完全由您自己完成
    亚马逊解析简单且完全由您自己完成
    I came across a script on the Internet that allows you to parse product cards from Amazon. And I just needed a solution to a problem like that. I wrac...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3