2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

教材

ShinCodeさんの

完全保存版】React Hooksを完全に理解するHooksマスター講座【React18~19対応】

を元に、私が学んだ内容をまとめていきますわ。

useContext

const value = useContext(SomeContext)

useContextは、Reactのコンポーネントでデータを深く受け渡す際に使用するフックですわ。コンポーネントの階層が深くなると、propsの受け渡しが複雑になりがちですの。その問題を解決するために、Contextを使ってデータを一元管理し、必要なコンポーネントでデータを参照することができますの。

Propsのバケツリレー

以下の図のように、深いコンポーネントツリーでデータを渡すために、propsを多くのコンポーネントを経由して渡す必要がありますの。

スクリーンショット 2024-06-05 20.13.06.png

Contextの使用

Contextを使用すると、データを一つの場所にまとめて管理し、任意のコンポーネントから参照できますの。

スクリーンショット 2024-06-05 20.13.15.png

ユースケース

  1. ユーザー認証

    ユーザー認証のためにContextを利用することがよくございますわ。例えば、ログインしているユーザー情報をContextで管理し、ヘッダーなどのコンポーネントでその情報を参照して表示を変更することができますの。このような場合、useContextフックを使用して、どのページでもユーザー情報を取得できるようにします。

  2. テーマの切り替え

    ダークモードやライトモードの切り替えにもContextが有用でございます。テーマの状態をContextで管理することで、アプリケーション全体で統一されたテーマを簡単に適用することができますわ。

認証プロバイダーとしてのuseContext

import { useState } from "react";

const Lesson4_1 = () => {
  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");

  const handleLogin = () => {};

  return (
    <div>
      <div>
        <p>ログイン済み:</p>
        <button>ログアウト</button>
      </div>

      <div>
        <input
          type="text"
          placeholder="Username"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <button onClick={handleLogin}>ログイン</button>
      </div>
    </div>
  );
};

export default Lesson4_1;

ログインしている状態なら「ログイン済み」を表示し、それ以外ならフォームを表示させたいのですわね。

useContext()で必要な要素

  1. Contextの作成

    Reactでデータをグローバルに管理するための仕組みですわ。通常、データを親コンポーネントから子コンポーネントにpropsで渡しますが、コンポーネント階層が深くなった時に簡単にデータを渡すことができますの。

    import { createContext } from 'react';
    
    // Contextの作成
    const AuthContext = createContext(null);
    

  2. Providerの作成

    Providerは、Contextで管理するデータをコンポーネントツリーに供給する役割を持つのですわ。これにより、アプリケーション全体や特定のコンポーネントにデータを渡すことができますの。

    このコードに関しては、次の章で詳しく解説しますわ。

    import { ReactNode, createContext, useContext, useState } from "react"
    
    type User = {
      id: string;
      username: string;
      email: string
    }
    
    interface AuthContextType {
      user: User | null;
      login: (userInfo: User) => void;
      logout: () => void;
    }
    
    const AuthContext = createContext<AuthContextType | undefined>(undefined);
    
    export const useAuth = () => {
      const context = useContext(AuthContext);
      return context;
    }
    
    // プロバイダーを作成する
    const AuthProvider = ({children}: {children :ReactNode}) => {
      const [user, setUser] = useState<User | null>(null);
    
      // ログイン・ログアウトを仮に実装する
      const login = (userInfo: User) => {
        if(userInfo.username === "testUser" && userInfo.email === "test@gmail.com") {
          setUser(userInfo);
        }
        else {
          console.log("ログインできません")
        }
      };
      const logout = () => {
        setUser(null)
      };
      // childrenはreact.nodeのこと
    
      const contextValue = {
        user,
        login,
        logout
      }
    
      return (
        <AuthContext.Provider value={contextValue}>
          {children}
        </AuthContext.Provider>
      )
    }
    
    export default AuthProvider;
    

  3. Providerでラップする

    App全体をProviderでラップするのですわ。

    AuthProviderでuseContextを反映させたいコンポーネントを囲むのです。

    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App.tsx";
    import "./index.css";
    import AuthProvider from "./lessons/Lesson4/context/AuthContext.tsx";
    
    ReactDOM.createRoot(document.getElementById("root")!).render(
      <React.StrictMode>
        <AuthProvider>
          <App />
        </AuthProvider>
      </React.StrictMode>
    );
    

  4. useContextを使ってContextの値を取得する

    作成したuseContextを使用したいコンポーネントで呼び出し、値を取得しますの。
    この場合だと、const {user, login, logout} = userAuth()の部分ですわ。

    import { useState } from "react";
    import { useAuth } from "../context/AuthContext";
    
    const Lesson4_1 = () => {
      const [username, setUsername] = useState("");
      const [email, setEmail] = useState("");
    
      const { user, login, logout } = useAuth();
    
      const handleLogin = () => {
        login({ id: "1", username, email });
      };
    
      return (
        <div>
          {user ? (
            <div>
              <p>ログイン済み:</p>
              <button onClick={logout}>ログアウト</button>
            </div>
          ) : (
            <div>
              <input
                type="text"
                placeholder="Username"
                value={username}
                onChange={(e) => setUsername(e.target.value)}
              />
              <input
                type="email"
                placeholder="Email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
              />
              <button onClick={handleLogin}>ログイン</button>
            </div>
          )}
        </div>
      );
    };
    
    export default Lesson4_1;
    
    

    スクリーンショット 2024-06-05 19.00.19.png
    スクリーンショット 2024-06-05 19.01.09.png

    このように、testUsertest@gmail.comの場合にログインすることができ、ログアウトボタンを押すとsetUserがnullになり、再度入力フォームに戻りますの。

    Providerのコード

    import { ReactNode, createContext, useContext, useState } from "react"
    
    type User = {
      id: string;
      username: string;
      email: string
    }
    
    interface AuthContextType {
      user: User | null;
      login: (userInfo: User) => void;
      logout: () => void;
    }
    
    const AuthContext = createContext<AuthContextType | undefined>(undefined);
    
    export const useAuth = () => {
      const context = useContext(AuthContext);
      if(context === undefined) {
        throw new Error("AuthProviderの中で宣言してください。")
      }
      return context;
    }
    
    // プロバイダーを作成する
    const AuthProvider = ({children}: {children :ReactNode}) => {
      const [user, setUser] = useState<User | null>(null);
    
      // ログイン・ログアウトを仮に実装する
      const login = (userInfo: User) => {
        if(userInfo.username === "testUser" && userInfo.email === "test@gmail.com") {
          setUser(userInfo);
        }
        else {
          console.log("ログインできません")
        }
      };
      const logout = () => {
        setUser(null)
      };
      // childrenはreact.nodeのこと
    
      const contextValue = {
        user,
        login,
        logout
      }
    
      return (
        <AuthContext.Provider value={contextValue}>
          {children}
        </AuthContext.Provider>
      )
    }
    
    export default AuthProvider;
    

    説明

    1. typescriptの定義

      type User = {
        id: string;
        username: string;
        email: string;
      }
      
      interface AuthContextType {
        user: User | null;
        login: (userInfo: User) => void;
        logout: () => void;
      }
      

      これは、TypeScriptでユーザー情報の型を定義していますの。

      type Userはユーザー情報の型を定義しており、AuthContextTypeでは、AuthContextの型を定義しています。

      このインターフェースは、提供する値とメソッドを指定しますわ。

    2. createContext

      const AuthContext = createContext<AuthContextType | undefined>(undefined);
      

      コンテキストを作成しますの。

      createContext<AuthContextType | undefined>(undefined)という形で型を指定しています。

    3. カスタムフックのUseAuth()

      export const useAuth = () => {
        const context = useContext(AuthContext);
        if (context === undefined) {
          throw new Error("AuthProviderの中で宣言してください。");
        }
        return context;
      };
      

      useContext(AuthContext)を使用し、変数contextにコンテキストの現在の値を取得しますわ。

      そして、contextを返しますわ。

      また、if文で、contextundefinedの場合に、エラーを返すようにしていますの。

    4. プロバイダーの作成

      const AuthProvider = ({children}: {children :ReactNode}) => {
      

      childrenAuthProviderの子要素を指し、{children :ReactNode}という型注釈で、childrenがReactノードであることを示していますわ。

      const [user, setUser] = useState<User | null>(null);
      

      userの状態をuseStateで管理しますわ。初期値はnullですの。

      const login = (userInfo: User) => {
        if(userInfo.username === "testUser" && userInfo.email === "test@gmail.com") {
          setUser(userInfo);
        } else {
          console.log("ログインできません");
        }
      };
      
      const logout = () => {
        setUser(null);
      };
      
      

      login関数は、userInfoを受け取って、それを元に条件分岐で、user状態を更新するか決めていますわ。また、logoutsetUsernullにするだけですわ。

      const contextValue = {
        user,
        login,
        logout
      };
      

      contextValueはコンテキストプロバイダーが提供する値を準備していますの。

      return (
        <AuthContext.Provider value={contextValue}>
          {children}
        </AuthContext.Provider>
      );
      
      

      最後に、AuthContext.ProvidercontextValueをコンテキストの値として設定しますわ。

      {children}をレンダリングすることで、AuthProviderの子要素がこのコンテキストにアクセスできるようになりますの。

      Provider内の関数や値のメモ化

      メモ化とは、関数や値をメモリとして一時的に保存することですの。これにより、不要な再レンダリングを減らし、パフォーマンスの向上が期待できるのですわ。

      現在の状態では、ログインすると再レンダリングが発生し、ログイン関数の再生成や値の再計算が行われる可能性があり、パフォーマンスに影響を与えるかもしれませんの。ログインやログアウトの関数はメモ化しておくと良いですわ。

        // ログイン・ログアウトを仮に実装する
        const login = useCallback((userInfo: User) => {
          if (
            userInfo.username === "testUser" &&
            userInfo.email === "test@gmail.com"
          ) {
            setUser(userInfo);
          } else {
            console.log("ログインできません");
          }
        }, []);
      
        const logout = useCallback(() => {
          setUser(null);
        }, []);
      
        const contextValue = useMemo(
          () => ({
            user,
            login,
            logout
          }),
          [user, login, logout]
        )
      
      1. useCallbackによる関数のメモ化

        useCallbackフックを使用し、loginlogout関数をメモ化していますの。これにより、依存配列が更新されない限り、これらの関数は同じインスタンスを保持し続け、際レンダリング時に再生成されませんの。

      2. useMemoによる値のメモ化

        useMemoフックを使用して、contextValueをメモ化していますの。これにより、userloginlogoutが変更されない限り、contextValueは再計算されませんの。

      まとめ

      useContextの概要と利用法

      useContextフックはReactのコンポーネントでデータを深く受け渡す際に使用し、propsのバケツリレー問題を解決しますの。Contextを使うことで、データを一元管理し、必要なコンポーネントで参照できるようにするのですわ。

      主要なユースケース

      useContextは、ユーザー認証やテーマの切り替えなどに使われることが多いですわ。認証情報やテーマ状態をContextで管理し、アプリケーション全体で共通の状態を保持できますの。

      Contextの作成とProviderの設定

      まずContextを作成し、それを提供するProviderを設定しますの。これにより、アプリケーション全体や特定のコンポーネントでデータを供給できるのですわ。AuthProviderは例として、ユーザー情報を管理するプロバイダーとして機能します。

      ProviderでのラップとuseContextの利用

      作成したProviderでアプリケーション全体をラップし、そのContextの値を利用するためにuseContextを使いますの。これにより、どのコンポーネントでも簡単に認証情報を取得し、操作できるようになるのですわ。

      メモ化によるパフォーマンス向上

      useCallbackuseMemoを使用して、関数や値をメモ化することで、不要な再レンダリングを防ぎ、パフォーマンスを向上させますの。これにより、loginlogoutの関数、及びcontextValueが変更されない限り、同じインスタンスを保持し続けますの。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?