教材
ShinCodeさんの
【完全保存版】React Hooksを完全に理解するHooksマスター講座【React18~19対応】
を元に、私が学んだ内容をまとめていきますわ。
useContext
const value = useContext(SomeContext)
useContext
は、Reactのコンポーネントでデータを深く受け渡す際に使用するフックですわ。コンポーネントの階層が深くなると、propsの受け渡しが複雑になりがちですの。その問題を解決するために、Context
を使ってデータを一元管理し、必要なコンポーネントでデータを参照することができますの。
Propsのバケツリレー
以下の図のように、深いコンポーネントツリーでデータを渡すために、propsを多くのコンポーネントを経由して渡す必要がありますの。
Contextの使用
Context
を使用すると、データを一つの場所にまとめて管理し、任意のコンポーネントから参照できますの。
ユースケース
-
ユーザー認証
ユーザー認証のために
Context
を利用することがよくございますわ。例えば、ログインしているユーザー情報をContext
で管理し、ヘッダーなどのコンポーネントでその情報を参照して表示を変更することができますの。このような場合、useContext
フックを使用して、どのページでもユーザー情報を取得できるようにします。
-
テーマの切り替え
ダークモードやライトモードの切り替えにも
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()で必要な要素
-
Contextの作成
Reactでデータをグローバルに管理するための仕組みですわ。通常、データを親コンポーネントから子コンポーネントにpropsで渡しますが、コンポーネント階層が深くなった時に簡単にデータを渡すことができますの。
import { createContext } from 'react'; // Contextの作成 const AuthContext = createContext(null);
-
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;
-
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> );
-
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;
このように、
testUser
とtest@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;
説明
-
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
の型を定義しています。このインターフェースは、提供する値とメソッドを指定しますわ。
-
createContext
const AuthContext = createContext<AuthContextType | undefined>(undefined);
コンテキストを作成しますの。
createContext<AuthContextType | undefined>(undefined)
という形で型を指定しています。
-
カスタムフックのUseAuth()
export const useAuth = () => { const context = useContext(AuthContext); if (context === undefined) { throw new Error("AuthProviderの中で宣言してください。"); } return context; };
useContext(AuthContext)
を使用し、変数context
にコンテキストの現在の値を取得しますわ。そして、
context
を返しますわ。また、
if
文で、context
がundefined
の場合に、エラーを返すようにしていますの。
-
プロバイダーの作成
const AuthProvider = ({children}: {children :ReactNode}) => {
children
はAuthProvider
の子要素を指し、{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
状態を更新するか決めていますわ。また、logout
はsetUser
をnull
にするだけですわ。const contextValue = { user, login, logout };
contextValue
はコンテキストプロバイダーが提供する値を準備していますの。return ( <AuthContext.Provider value={contextValue}> {children} </AuthContext.Provider> );
最後に、
AuthContext.Provider
でcontextValue
をコンテキストの値として設定しますわ。{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] )
-
useCallbackによる関数のメモ化
useCallback
フックを使用し、login
とlogout
関数をメモ化していますの。これにより、依存配列が更新されない限り、これらの関数は同じインスタンスを保持し続け、際レンダリング時に再生成されませんの。
-
useMemoによる値のメモ化
useMemo
フックを使用して、contextValue
をメモ化していますの。これにより、user
、login
、logout
が変更されない限り、contextValue
は再計算されませんの。
まとめ
useContextの概要と利用法
useContext
フックはReactのコンポーネントでデータを深く受け渡す際に使用し、propsのバケツリレー問題を解決しますの。Context
を使うことで、データを一元管理し、必要なコンポーネントで参照できるようにするのですわ。主要なユースケース
useContext
は、ユーザー認証やテーマの切り替えなどに使われることが多いですわ。認証情報やテーマ状態をContext
で管理し、アプリケーション全体で共通の状態を保持できますの。Contextの作成とProviderの設定
まず
Context
を作成し、それを提供するProvider
を設定しますの。これにより、アプリケーション全体や特定のコンポーネントでデータを供給できるのですわ。AuthProvider
は例として、ユーザー情報を管理するプロバイダーとして機能します。ProviderでのラップとuseContextの利用
作成した
Provider
でアプリケーション全体をラップし、そのContext
の値を利用するためにuseContext
を使いますの。これにより、どのコンポーネントでも簡単に認証情報を取得し、操作できるようになるのですわ。メモ化によるパフォーマンス向上
useCallback
とuseMemo
を使用して、関数や値をメモ化することで、不要な再レンダリングを防ぎ、パフォーマンスを向上させますの。これにより、login
やlogout
の関数、及びcontextValue
が変更されない限り、同じインスタンスを保持し続けますの。 -
-