React での状態管理: 実践的な例

反応、状態管理、コンテキスト API
React での状態管理: 実践的な例 cover image

React は、多くの開発者にとって動的なクライアント側アプリケーションを構築するための頼りになるフレームワークです。これらのアプリケーションの動的な性質は、クライアント側で可能な柔軟性と機能の拡張リストに由来しており、これにより開発者はブラウザ上に読み込まれる本格的なアプリケーションを数秒で構築できるようになりました。これは、これまで実現できなかった偉業です。静的 Web の時代には可能でした (または非常に面倒でした)。

この可能性の拡大に伴い、状態管理の概念が登場しました。クライアント側アプリケーションの複雑さが増すにつれて、ローカル状態を維持する必要性が増大し、正しく処理され、スケーラビリティを念頭に置いて考えられないと、それ自体がボトルネックになります。

この問題は、さまざまなアプローチに従い、さまざまなサブ問題のセットに焦点を当てた多くのフレームワークによって対処されています。そのため、各アプリケーションのニーズを評価し、それらに従った適切なアプローチを採用するには、選択したフレームワークのエコシステムを高度に理解することが重要です。メトリクス。この記事では、一般的な状態管理の問題の概要を説明し、それへの対応としてさまざまなアプローチ (useState、Context API) を紹介します。この記事では複数の解決策を紹介しますが、より小規模な課題のみに焦点を当てます。今後の記事では、より高度な主題について取り上げます。

認証ワークフロー

記事全体で紹介されているコードは、こちらでご覧いただけます。

ライブ プレビュー リンクには、こちらからアクセスできます。

React アプリケーションの認証プロセスを実装する場合を考えてみましょう。

User Login

上の GIF に示すように、ユーザーが資格情報を使用してアプリケーションにログインまたはサインアップできるようにしたいと考えています。有効な資格情報が提供された場合、ユーザーはログインし、アプリケーションは自動的にホームページに移動し、ユーザーはアプリケーションの使用を続行できます。

同様に、ユーザーがログアウトすると、ホームページのリソースはログイン後に保護され、ログイン ページがユーザーがアクセスできる唯一のページになります。

このワークフローを実装の観点から考えると、App という名前のメイン コンポーネントがあり、App コンポーネントはユーザーを 2 つのページ (ホームまたはログイン) のいずれかにリダイレクトします。ユーザーの現在の状態 (ログイン、ログアウト) によってどちらが決定されるかが決まります。ユーザーがリダイレクトされるページでは、ユーザーの現在の状態が変化する (たとえば、ログインからログアウトへの変化) と、対応するページへの即時リダイレクトがトリガーされます。

State

上の図に示すように、App コンポーネントが現在の状態を考慮し、その現在の状態に基づいて 2 つのページのうちの 1 つ (ホームまたはログイン) のみをレンダリングするようにします。

ユーザーが null の場合は、認証されたユーザーが存在しないことを意味するため、自動的にログイン ページに移動し、条件付きレンダリングを使用してホームページを保護します。ユーザーが存在する場合は、その逆を行います。

何を実装すべきかをしっかりと理解したので、いくつかのオプションを検討してみましょう。まず最初に React プロジェクトをセットアップしましょう。

プロジェクト リポジトリには、フロントエンド側の実装に使用するサンプル バックエンド アプリケーションが含まれています (ここでは主な焦点ではないため説明しませんが、コードは意図的にシンプルに保たれているため、苦労することはありません) )

まず、次のページとコンポーネントを作成します。

  • ホームページ

  • ログインページ

  • ユーザーの編集ページ

  • ナビゲーションバーコンポーネント

  • ユーザードロップダウンコンポーネント

複数のページを持つ React アプリケーションには適切なナビゲーションが必要です。そのためには、react-router-dom を使用してグローバル ブラウザー ルーター コンテキストを作成し、さまざまな React ルートを登録できます。


yarn add react-router-dom

多くの読者がチュートリアルに従うことを好むことを私たちは知っているので、ここではすぐに理解できるスターター テンプレートを紹介します。このスターター ブランチは、事前定義された TailwindCSS JSX コンポーネントに DaisyUI を使用します。これには、すべてのコンポーネント、ページ、およびセットアップ済みのルーターが含まれます。このチュートリアルに従って、簡単な手順に従って認証フロー全体を自分で構築することを検討している場合は、まずリポジトリをフォークすることから始めてください。リポジトリをフォークした後、クローンを作成し、start-hereブランチ: から開始します。


git clone git@github.com:<your-username>/fullstack-resourcify.git

start-here ブランチをプルしたら、次のようになります。

  • 好みのコードエディターでプロジェクトを開きます

  • ディレクトリを frontend/ に変更します

  • 依存関係をインストールします: 糸

  • 開発サーバーを起動します:yarn dev·

プレビューは次のようになります。

Changing user name

Navbar (右上) に表示される名前は、メインのアプリ コンポーネントで定義された状態変数です。同じ変数が Navbar と Home ページの両方に渡されます。上記で使用した単純なフォームは、実際に EditPage コンポーネントから「name」状態変数を更新します。

以下に示す 2 つのアプローチで、実装の詳細を説明します。

最初のアプローチ: useState

useState()

useState は最も一般的に使用される React フックの 1 つで、React Functional コンポーネントで状態を作成および変更できます。 useState コンポーネントの実装は非常にシンプルで、使いやすいです。新しい状態を作成するには、状態の初期値を指定して useState を呼び出す必要があります。useState フックは 2 つの変数を含む配列を返します。最初の変数は状態です。 1 つは状態を参照するために使用できる変数、もう 1 つは状態の値を変更するために使用する関数です。非常に簡単です。

実際にそれを見てみるのはどうでしょうか? Navbar (右上) に表示される名前は、メインのアプリ コンポーネントで定義された状態変数です。同じ変数が Navbar と Home ページの両方に渡されます。上記で使用した単純なフォームは、実際に EditPage コンポーネントから「name」状態変数を更新します。結論は次のとおりです。 useState は、初期状態をパラメータとして受け入れ、2 つの値を保持する 2 つの変数、初期状態を含む状態変数、および同じ状態変数のセッター関数を返すコア フックです。

それを分解して、そもそもそれがどのように実装されたかを見てみましょう。

1.「name」状態変数を作成します。

./src/App.jsx


import { useState } from "react";

function App() {
const testValue = "CLA";
 //using the useState hook to create a state variable out of an initial value passed as an argument
 const [name, setName] = useState(testValue);
 console.log(`Rendering: &#36;{name}`);

 return (...)};

小道具

Props は、React コンポーネントの基本的な構成要素の 1 つです。概念的には、React 機能コンポーネントを Javascript 関数として考える場合、Props は関数パラメーターにすぎません。Props と useState フックを組み合わせることで、強固なフレームワークを提供できます。単純な React アプリケーション全体の状態を管理します。

React プロパティは属性としてカスタム コンポーネントに渡されます。 props として渡された属性は、次のように、引数として受け入れるときに props オブジェクトから構造化できます。

小道具を渡す

<Routes>
  <Route path="/" element={<Home name={name} />} /> // Passing name as a prop to
  Home Component
  <Route
    path="/user"
    element={<EditUser name={name} setName={setName} />} // passing both name and setItem function as props to EditUser component
  />
  <Route path="/login" element={<Login />} />
</Routes>

通常の関数の引数と同様に、関数コンポーネント内で Props を受け入れて使用できます。 「名前」がプロップとして Home コンポーネントに渡されるため、同じコンポーネント内でレンダリングできます。次の例では、構造化構文を使用して渡された prop を受け入れ、props オブジェクトから name プロパティを抽出しています。

小道具の受け入れ

./src/pages/Home.jsx

... ...
export default function Home({name}) { //Destructuring the name property from the props object, another approach would be: Home(props.name)
 console.log("Rendering: Home");
 return (
   <div className="flex flex-col bg-white m-auto p-auto">
     <h1 className="flex py-5 lg:px-20 md:px-10 mx-5 font-bold text-2xl text-gray-800">
       Welcome {name}
     </h1>
... ...

プロのヒント

ブラウザのコンソールを開き、状態が変化したときに「name」プロパティを使用するすべてのコンポーネントがどのように再レンダリングされるかを確認します。状態変数を操作するとき、React は次の状態を保存し、新しい値でコンポーネントを再度レンダリングし、UI を更新します。

Components Re-rendering

useState の欠点

小道具の穴あけ

プロップドリルは、コンポーネントのセットが親コンポーネントによって提供される特定のプロップを必要とするコンポーネントの階層を指す用語です。経験の浅い開発者が通常使用する一般的な回避策は、これらのプロップをコンポーネントのチェーン全体に渡すことです。これに関する問題は、これらの props のいずれかを変更すると、コンポーネントのチェーン全体が再レンダリングされ、これらの不必要なレンダリング、つまりこれらの props を必要としないチェーンの途中にあるコンポーネントの結果としてアプリケーション全体の速度が実質的に低下するというアプローチです。小道具を転送するための媒体として機能します。

例:

  • 状態変数は useState() フックを使用してメイン アプリ コンポーネントで定義されました

  • 名前プロパティが Navbar コンポーネントに渡されました

  • 同じ prop が Navbar で受け入れられ、もう一度 prop として UserDropdown コンポーネントに渡されました

  • UserDropdown は、prop を受け入れる最後の子要素です。

Prop-drilling

./src/App.jsx

... ...
function App() {
 const [name, setName] = useState(test);
 console.log("Rendering: App");

 return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar name={name} />

       <main className="px-4">
... ...

./src/components/Navbar.jsx

import React from "react";
import Logo from "../assets/cla.svg";
import { BiSearchAlt } from "react-icons/bi";
import { Link } from "react-router-dom";
import UserDropdown from "./UserDropdown";

export default function Navbar({ name }) {
  console.log("Rendering: Navbar");

  return (
    <>
      <div className="navbar bg-base-100 drop-shadow-sm">
        <div className="flex-1">
          <Link
            to="/"
            className="btn btn-ghost normal-case text-md md:text-xl px-2 gap-1"
          >
            <img src={Logo} className="h-6" alt="" />
            Resources
          </Link>
        </div>

        <UserDropdown name={name} />
      </div>
    </>
  );
}

すべて同じ状態を渡してレンダリングするコンポーネントのさまざまなレイヤーを含む React アプリを維持するのがどれほど難しいかを想像してみてください。

増大する複雑さとコード品質

React アプリケーションでの状態管理の唯一の手段として useState と props を使用すると、コードベースが急速に複雑になり、数十または数百の状態変数を管理する必要が生じます。状態変数は相互に重複する可能性があり、さまざまなファイルやファイルに分散している可能性があります。コンポーネントの変更は非常に困難な場合があるため、特定の状態変数を変更する場合は、すでに遅いアプリケーションで追加の再レンダリングが行われる可能性を回避するために、コンポーネント間の依存関係を慎重に検討する必要があります。

2 番目のアプローチ: コンテキスト API

Context API は、状態管理に props と useState のみを使用することの欠点を解決しようとする React の試みです。特に、コンテキスト API は、コンポーネント ツリー全体に props を渡す必要性に関する前述の問題に対する答えとして提供されます。コンテキストを使用すると、グローバルと見なされるデータの状態を定義し、コンポーネント ツリー内の任意のポイントからその状態にアクセスできるため、プロップドリルの必要はもうありません。

コンテキスト API は、共有する必要がある他の種類のデータのグローバル データ共有、UI テーマ、ユースケースである認証情報、言語などのデータの問題を解決するために最初に考案されたことを指摘することが重要です。複数のコンポーネント間で使用されますが、必ずしもすべてのアプリケーションにグローバルであるとは限りません。ユースケースによっては、コンテキストが最適なオプションではない可能性があります。コンポーネント構成 などの他の手法を検討することもできます。 /composition-vs-inheritance.html) これはこの記事の範囲外です。

コンテキストへの切り替え

認証コンテキストの作成

createContext は簡単で、新しいコンテキスト変数を作成し、単一のオプションのパラメーター (コンテキスト変数のデフォルト値) を受け取ります。

これを実際に見てみましょう。まず新しいフォルダー「contexts」を作成し、その中に新しいファイル「Auth.jsx」を作成します。新しいコンテキストを作成するには、createContext() 関数を呼び出し、返された値を次にエクスポートされる新しい変数 Auth に割り当てる必要があります。

./src/contexts/Auth.jsx

import { createContext } from "react";

export const Auth = createContext();

認証コンテキストを指定します

次に、前に作成した「Auth」コンテキスト変数を公開する必要があります。これを実現するには、コンテキスト プロバイダーを使用します。コンテキスト プロバイダーは、「値」プロパティを持つコンポーネントです。このプロパティを使用して、値 - 名前を共有できます。 – コンポーネント ツリー全体で、コンポーネント ツリーをプロバイダー コンポーネントでラップすることで、その prop を各子コンポーネントに個別に渡す必要がなく、コンポーネント ツリー内のどこからでもその値にアクセスできるようになります。

この認証の例では、ユーザー オブジェクトを保持する変数と、その状態変数を操作するための何らかのセッターが必要です。これは useState の完璧な使用例です。 Context を使用する場合は、提供するデータを定義し、そのデータ (この例では user) を内部にネストされているすべてのコンポーネント ツリーに渡す必要があるため、プロバイダー コンポーネント内で新しい状態変数 user を定義し、最後に、プロバイダーがコンポーネント ツリーに公開する値として配列内の user と setUser の両方を渡します。

./src/contexts/Auth.jsx

import { createContext, useState } from "react";

export const Auth = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  return <Auth.Provider value={[user, setUser]}>{children}</Auth.Provider>;
};

もう 1 つできることは、「name」状態変数をアプリのメイン コンポーネントから認証コンテキストに移動し、ネストされたコンポーネントに公開することです。

import { createContext, useState } from "react";

export const Auth = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [name, setName] = useState("CLA");
  return <Auth.Provider value={[name, setName]}>{children}</Auth.Provider>;
};

あとは、エクスポートしたのと同じ AuthProvider コンポーネントにアプリをネストするだけです。

./src/main.jsx:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { AuthProvider } from "./contexts/Auth";

import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <AuthProvider>
    <App />
  </AuthProvider>
);

Auth.Provider 内で子プロパティをレンダリングしているため、AuthProvider コンポーネント内にネストされているすべての要素は、Auth.Provider に渡した値プロパティを使用できるようになりました。ややこしいように思えるかもしれませんが、一度試してみて、グローバルなコンテキスト状態を提供したり消費したりしてみてください。結局のところ、Context API を試してみて初めて理解できました。

認証コンテキストの使用

最後のステップは簡単です。コンテキスト フック「useContext」を使用して、「Auth」のコンテキスト プロバイダーが提供する値にアクセスします。この場合、これは user と setUser を含む配列です。次のコードで、次のことが可能です。 useContext を使用して、Navbar 内の認証コンテキストを使用します。これが可能なのは、Navbar が App コンポーネント内にネストされており、AuthProvider が App コンポーネントをラップしているため、value prop は useContext フックのみを使用して使用できるためです。 React が提供するもう 1 つの素晴らしいツールは、グローバルにアクセスでき、任意のコンシューマ コンポーネントによって操作できるデータを管理するために、すぐに使用できるツールです。

./src/components/Navbar.jsx

export default function Navbar() {
 const [name, setName] = useContext(Auth);
 console.log(name)

 return (...)};

Navbar() 関数コンポーネントで props が受け入れられなくなっていることに注目してください。代わりに useContext(Auth) を使用して認証コンテキストを消費し、name と setName の両方を取得します。これは、プロップを Navbar に渡す必要がなくなったことも意味します。

./src/App.jsx

// ... //
return (
   <BrowserRouter>
     <div className="h-screen">
       <Navbar/> // no need to pass prop anymore
// ... //

認証コンテキストの更新

提供されている setName 関数を使用して、「name」状態変数を操作することもできます。

./src/pages/EditUser.jsx

export default function EditUser() { // no need to accept props anymore
 const [name, setName] = useContext(Auth); // grabbing the name and setName variables from Auth context
 console.log("Rendering: EditUser");

useReducer() フックの導入

前の例では、コンテキスト API を使用して状態を管理し、コンポーネント ツリーで共有しました。ロジックのベースとして useState をまだ使用していることに気づいたかもしれません。状態はまだ単純なオブジェクトであるため、これはほとんど問題ありません。この時点ではまだ可能ですが、認証フローの機能を拡張する場合は、現在ログインしているユーザーの電子メール以上のものを保存する必要があることは間違いありません。ここで、以前に行った制限に戻ります。複雑な状態で useState を使用すると、幸いなことに、React は複雑な状態を管理するための useState の代替手段を提供することでこの問題を解決します。 useReducer を入力します。

useReducer は useState の一般化されたバージョンと考えることができ、reducer 関数と初期状態という 2 つのパラメーターを取ります。

初期状態については注目すべき興味深い点はありません。魔法はリデューサー関数の内部で起こります。発生したアクションのタイプをチェックし、そのアクションに応じて、リデューサーは状態に適用する更新を決定し、その新しい値を返します。 。

以下のコードを見ると、reducer 関数には 2 つの可能なアクション タイプがあることがわかります。

  • "LOGIN": この場合、ユーザー状態は、アクション ペイロード内で提供される新しいユーザー資格情報で更新されます。

  • "LOGOUT": この場合、ユーザー状態はローカル ストレージから削除され、null に戻されます。

アクション オブジェクトには、適用するロジックを決定するタイプ フィールドと、そのロジックの適用に必要なデータを提供するオプションのペイロード フィールドの両方が含まれることに注意することが重要です。

最後に、useReducer フックは現在の状態と、リデューサーにアクションを渡すために使用するディスパッチ関数を返します。

残りのロジックは前の例と同じです。

./src/contexts/Auth.jsx

import { createContext, useEffect, useReducer, useState } from "react";

export const Auth = createContext();
import { createContext, useReducer } from "react";

export const Auth = createContext();
const reducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      return { user: action.payload };

    case "LOGOUT":
      localStorage.removeItem("user");
      return { user: null };

    default:
      break;
  }
};

export const AuthProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, {
    user: null,
  });

  return (
    <Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
  );
};

setState セッター関数を使用する代わりにアクションをディスパッチする – 例: setName –

先ほど述べたように、ディスパッチ関数を使用してリデューサーにアクションを渡します。次のコードでは、LOGIN アクションを開始し、ユーザーの電子メールをペイロードとして提供します。これで、ユーザーの状態が更新され、この変更がトリガーされます。ユーザー状態にサブスクライブされたすべてのコンポーネントの再レンダリング。再レンダリングは状態に実際の変更が発生した場合にのみトリガーされ、Reducer が以前と同じ状態を返す場合は再レンダリングは行われないことに注意することが重要です。

export default function Login() {
 const { dispatch } = useContext(Auth);


 const handleLogin = async (e) => {
   // Updating the global Auth context
   dispatch({ type: "LOGIN", payload: {email: email.current.value} });
 };

 return (...)};

ユーザーログイン

JWT Local storage

プロのヒント

ログインに成功した後に受け取るユーザー オブジェクトが localStorage にどのように保存されるかに注目してください。

ログイン用のカスタムフック

useReducer を十分に理解したので、ログイン フックとログアウト ロジックを独自の個別のカスタム フックにさらにカプセル化できます。ログイン フックへの 1 回の呼び出しで、ログイン ルートへの API 呼び出しを処理し、新しいユーザーを取得できます。認証情報を取得してローカル ストレージに保存し、LOGIN 呼び出しをディスパッチしてユーザー状態を更新しながら、エラー処理を処理します。

./src/hooks/useLogin.jsx

import { useContext, useState } from "react";
import { Auth } from "../contexts/Auth";

export const useLogin = () => {
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  const { dispatch } = useContext(Auth);

  const login = async (email, password) => {
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch("/api/users/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });

      const json = await response.json();

      if (json.name === "Error") {
        setError(json.message);
        setIsLoading(false);
      }

      if (!response.ok) {
        setIsLoading(false);
        setError(json);
      }

      if (response.ok) {
        // Save the user and token in the localstorage
        localStorage.setItem("user", JSON.stringify(json));

        // Updating the global Auth context
        dispatch({ type: "LOGIN", payload: json });

        setIsLoading(false);
      }
    } catch (error) {
      console.log(error);
    }
  };

  return { error, isLoading, login };
};

注: 読者のうちのより上級の React ユーザーは、なぜ useReducer の遅延初期化機能を使用してユーザー資格情報を取得しなかったのかと不思議に思っているかもしれません。useReducer は init 関数と呼ばれる 3 番目のオプションのパラメーターを受け入れます。この関数は次の場合に使用されます。状態の初期値を取得する前に、いくつかのロジックを適用する必要があります。これを選択しなかった理由は、関心の分離という単純な問題です。この方法のコードは理解するのが簡単で、その結果、保守も簡単です。 。

ログインページ

useLogin() フックを使用してログイン関数を抽出し、ユーザーが送信した資格情報を使用してログイン関数を呼び出した後のログイン ページの上部は次のようになります。

// ... ... //
export default function Login() {
 const { login, isLoading, error } = useLogin();
 console.log("Rendering: Login");
 const email = createRef(null);
 const password = createRef(null);

 const handleLogin = async (e) => {
   await login(email.current.value, password.current.value);
 };
 return (...)
// ... ... //

また、ユーザーがフォームを送信するときの送信機能も無効にします。

<button
  onClick={handleLogin}
  disabled={isLoading}
  className="btn btn-square w-full bg-gray-100 text-gray-600 hover:bg-gray-300 border-none"
>
  {isLoading && "A moment please!"}
  {!isLoading && "Login"}
</button>

バックエンドから受け取った認証エラーをレンダリングします。

{
  error && <span className="text-red-500 p-2">{error.message}</span>;
}

Rendering Errors

ユーザー状態の維持

なぜユーザー オブジェクトを localStorage に保存する必要があるのか​​疑問に思われるかもしれません。簡単に言うと、トークンの有効期限が切れない限り、ユーザーをサインインしたままにしたいのです。この例のように、localStorage の使用は、JSON のビットを保存する優れた方法です。ログイン後にページを更新すると、状態がどのように消去されるかに注目してください。これは、 useEffect フックを使用して簡単に解決でき、localStorage に保存されたユーザー オブジェクトがあるかどうかを確認します。存在する場合は、ユーザーを自動的にログインします。

// ... ... //
export const AuthProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, {
    user: null,
  });

  useEffect(() => {
    const user = JSON.parse(localStorage.getItem("user"));

    if (user) {
      return dispatch({ type: "LOGIN", payload: user });
    }
  }, []);

  return (
    <Auth.Provider value={{ ...state, dispatch }}>{children}</Auth.Provider>
  );
};

ログアウト用のカスタムフック

同じことが Logout フックにも当てはまります。ここでは、現在のユーザー資格情報を状態とローカル ストレージの両方から削除するために、LOGOUT アクションをディスパッチしています。

./src/hooks/useLogout.jsx

import { useContext } from "react";
import { Auth } from "../contexts/Auth";

export const useLogout = () => {
  const { dispatch } = useContext(Auth);

  const logout = () => {
    // delete user from the localstorage
    localStorage.removeItem("user");
    // Wipe out the Auth context (user:null) / dipatch 'LOGOUT'
    dispatch({ type: "LOGOUT" });
  };

  return { logout };
};

ユーザーのログアウト

ユーザーをログアウトするには、UserDropdown.jsx にある [ログアウト] ボタンにクリック イベントを追加し、それに応じて処理しましょう。

./src/components/UserDropdown.jsx

// Extracting the logout function from useLogout() and handling the click event listener //
export default function UserDropdown() {
 const { user } = useContext(Auth);
 const { logout } = useLogout();
 console.log("Rendering: UserDropdown");

 const handleLogout = () => {
   logout();
 };
// ... ... //

// Adding a click event listener to logout button //
<li>
   <button onClick={handleLogout}>Logout</button>
</li>
// ... ... //

User Logout

React ルートの保護

アプリケーションを実装する最後のステップは、グローバル ユーザー状態を利用してユーザー ナビゲーションを制御することです。これは、どのような動作を実現する必要があるかを簡単に思い出させるものです。最初にユーザーはログイン ページに迎えられ、その時点からユーザーはホームページにのみアクセスできるようになります。ログインに成功した後、同様に、ユーザーはログアウト時にログイン ページにリダイレクトされます。

これは、react-router-dom ライブラリの助けを借りて、「/」と「/login」という 2 つのルートを定義することで実現されます。グローバル認証状態を使用して、各ルートでどのコンポーネントをレンダリングするかを制御します。 、null に評価される認証は認証されていないユーザーを表し、その逆も同様です。

./src/App.jsx

import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import "./App.css";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
import Login from "./pages/Login";
import { useContext } from "react";
import { Auth } from "./contexts/Auth";

function App() {
  const { user } = useContext(Auth);
  return (
    <BrowserRouter>
      <div className="h-screen">
        <Navbar />
        <main className="px-4">
          <Routes>
            <Route
              path="/"
              element={user ? <Home /> : <Navigate to="/login" />}
            />
            <Route
              path="/login"
              element={!user ? <Login /> : <Navigate to="/" />}
            />
          </Routes>
        </main>
      </div>
    </BrowserRouter>
  );
}

export default App;

図の要約

Diagram

## まとめ

この記事では、認証ワークフローの状態管理を実装するというシンプルだが非常に一般的な使用例に取り組み、さまざまなアプローチ、それぞれの背後にある理論的根拠、およびそのトレードオフを検討しました。クライアント側フレームワーク、特に React における状態管理は、フロントエンド コミュニティで最も話題になっているトピックの 1 つです。それは、それがアプリケーションのパフォーマンスとスケーラビリティを左右する可能性があるためです。この状態管理の問題を解決しようとするさまざまなテクニック、パターン、ライブラリ、ツールの量は膨大です。私たちの目標は、プラクティスをしっかりと理解していただき、より高度な目的で独自のアプリケーションに実装できるようにすることでした。より複雑なアプリケーションにおける状態管理のテクニックとパターンについては、React でのスケーラブルな状態管理のための Redux について説明する次の記事をご覧ください。

## 近日公開

ContextAPI と Redux ツールキットの比較

Redux は、Redux ツールキットが登場する前にコミュニティで長年採用されてきました。実際、RTK は、新しいアプリケーションで Redux 状態管理をブートストラップするためのスターター テンプレートとして導入されました (2019 年 10 月の初期名は「redux-starter-kit」でした)。現在、Redux のメンテナとコミュニティの間では、Redux ツールキットが redux を操作する有効な方法であるという一般的なコンセンサスが得られています。RTX は Redux ロジックの多くを抽象化しており、冗長さがはるかに少なく使いやすくなり、開発者が次のことを行うよう奨励します。ベスト プラクティスに従ってください。2 つの主な違いの 1 つは、Redux が意見にとらわれないように構築されており、最小限の API を提供し、一般的なタスク用に独自のライブラリを作成し、コード構造を処理することで、開発者が重労働のほとんどを行うことを期待していることです。これにより、開発時間が遅くなり、コードが複雑になりました。Redux ツールキットは、開発者がよくある落とし穴に陥ることを防ぐための抽象化の追加層として追加されました。詳細な洞察とメンテナによる推論については、公式ドキュメントを参照してください。こちら


Career Services background pattern

キャリアサービス

Contact Section background image

連絡を取り合いましょう

Code Labs Academy © 2024 無断転載を禁じます.