ReactJ を使用してフルスタック Web アプリを構築した初心者の頃、フロントエンドで認証を処理する方法について混乱していることに気づきました。つまり、バックエンドからアクセス トークンを受け取った後、次に何をすべきでしょうか?ログイン状態をどのように保存しますか?
ほとんどの初心者は、「ああ、トークンを状態に保存すればいいだけだ」と考えるでしょう。しかし、それが最良の解決策ではなく、まったく解決策でもないことがすぐにわかりました。なぜなら、ほとんどの経験豊富な ReactJs 開発者が知っているように、状態はページを更新するたびにクリアされるため一時的なものであり、絶対に解決できないからです。ユーザーが更新するたびにログインする必要はありません。
早送りして、React でフルスタック アプリを構築し、より経験豊富な開発者の認証アプローチを研究し、他の 2 つのアプリケーションでプロセスを複製する経験を少し積んだので、ガイドを示したいと思います。私が今どのように対処しているかについて。それが最善の方法だとは思わない人もいるかもしれませんが、私は今のところそれを自分の方法として採用しており、他の開発者が使用している他の方法を学ぶことに前向きです。
電子メールとパスワード (基本的な電子メールとパスワード認証を使用していると仮定して) をバックエンドに送信して、認証プロセスを開始しました。この記事はフロントエンドのみで認証を処理する方法について説明しているため、バックエンドで認証がどのように処理されるかについては説明しません。 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() }
アプリケーション全体、または認証コンテキストプロバイダーの認証状態へのアクセスが必要な部分だけをラップします。これは通常、ルートの App.jsx ファイルで行われます。コンテキスト 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」という 2 つの状態変数を初期化する必要があります。最初の状態はブール型で、最初は「false」に設定され、ログインが確認されると「true」に更新されます。 2 番目の状態変数は、名前、電子メールなどのログイン ユーザーの情報を保存するために使用されます。これらの状態変数は、条件付きレンダリングのためにアプリケーション全体でアクセスできるように、コンテキスト コンポーネントで返されるプロバイダーの値に含める必要があります。 .
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 を提供します。その際、状態値を別のファイルで初期化し、状態を利用または更新したいコンポーネントにそれをインポートします。ただし、ステップ 1 の HTTP 応答で受信した認証トークンを保存するために、nanostore/persistent を使用します。このパッケージは、localStorage に状態を保存することで状態を保持します。これにより、ページを更新しても状態が消去されなくなります。 @nanostores/react は、ナノストアの反応固有の統合であり、ナノストアの状態から値を抽出するための useStore フックを利用できるようにします。
それでは、次に進んでください:
次のパッケージをインストールします: nanostores、@nanostores/persistent、および @nanostores/react。
user.atom.js または任意の名前を付けた別のファイルで、nanostores/persistent.
それらをログイン フォーム コンポーネント ファイルにインポートし、ログイン応答で受信したトークンとユーザー データで状態を更新します。
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 フックを初期化し、フック内でトークンが 'authToken' ストアに存在するかどうかをチェックする 'checkLogin()' 関数を作成します。トークンが 'authToken' ストアに存在する場合は、関数を実行してトークンが存在するかどうかを確認します。期限切れ。チェックの結果に基づいて、ユーザーをログイン ページにリダイレクトして認証を受けるか、「isLoggedIn」状態を true に設定します。ログイン状態がより頻繁に追跡されるようにするために、現在のパスが変更されるたびにこのフックが実行されるように設定できます。これにより、アプリとの対話中にトークンの有効期限が切れた場合、ユーザーは追い出されるか、ログイン ページにリダイレクトされます。 。
アプリがロードまたは更新されるたびに、authToken ストア内のトークンを使用してバックエンドからユーザー情報を取得する関数を含む別の useEffect フックを初期化します。成功した応答を受け取った場合は、「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} ) }
ステップ 5 で作成したこれら 2 つの 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」ストアを null に設定してクリアする必要があることに注意してください。また、「isLoggedIn」を false に設定し、「authenticatedUser」を null に設定する必要もあります。
読んでいただきありがとうございます!
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3