useContextとは?
通常親コンポーネントから子コンポーネントにデータを渡す際はpropsを介して行います。しかし親から子、そのまた子といったように複数のコンポーネントを介してデータを渡す場合にpropsで渡すことが難しくなってきます。
useContextを使用することで、propsをコンポーネント間で渡すことなく全コンポーネント間でデータを共有することができます。
実際にuseContextを使ってコンポーネント間でデータを共有してみよう
ログイン画面で入力したユーザIDを、他の画面でも参照できるように Context
情報を共有する実装を考えました。
import { createContext, useContext, useState, ReactNode } from "react";
// userIdとそれを更新する関数を持つContextを作成
// 初期値は undefined(Providerの外で使われるとエラーにするため)
const UserIdContext = createContext<
{ userId: string; setUserId: (u: string) => void } | undefined
>(undefined);
// ユーザーIDの状態を提供するProviderコンポーネント
export const UserIdProvider = ({ children }: { children: ReactNode }) => {
const [userId, setUserId] = useState<string>(""); // userIdの状態管理
console.log(userId); // 状態が変わるたびにログに出力(デバッグ用)
return (
<UserIdContext.Provider value={{ userId, setUserId }}>
{children}
</UserIdContext.Provider>
);
};
// ContextからuserIdとsetUserIdを取り出すためのカスタムフック
export const useUserId = () => {
const context = useContext(UserIdContext);
// Contextがundefinedなら(=Providerの外から呼び出されたら)例外を投げる
if (!context) throw new Error("useUserId must be used within UserIdProvider");
return context;
};
ここで大事なのは
① createContext
で画面同士で共有したいステート(データ)を含んだ Context
情報を作成する。ここではユーザーIDを画面間で共有したいので userId
ステートを Context
に含める。
② Context
の値を更新するために必要な Provider
コンポーネントを作成する。この Provider
コンポーネントが更新された時に、Provider
に渡している 最新のvalue
の値を使って再レンダリングを発生させます。
③各画面でステート(データ)を参照したい時に使用するフック(useUserId
)を作成する。
Providerはこうやって使用する
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router";
import { UserIdProvider } from "./contexts/UserIdContext";
createRoot(document.getElementById("root")!).render(
<UserIdProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</UserIdProvider>,
);
import { useUserId } from "../contexts/UserIdContext";
export const Sample = () => {
const { setUserId } = useUserId();
const { userId } = useUserId();
setUserId("testID");
return <>{userId}</>;
}
このようにアプリ全体を UserIdProvider
で囲います。
useUserId
(カスタムフック)を使用して取得した setUserId
で userId
を更新することによって⇩
UserIdProvider
コンポーネント内の userId
が更新され再レンダリングが発生します。
アプリ全体を UserIdProvider
で囲っているため、囲っている配下のすべてのコンポーネントに再レンダリングが走り、ステートが更新されます。
全てのコンポーネントで最新のステートが反映されるため、どのコンポーネントでも最新のユーザーIDが参照できるという仕組みです。
まとめ
useContextは、単純にステートを更新する範囲を広げて、どのコンポーネントからでも参照できるようにするというもの。
propsで渡しているコンポーネントの範囲に抑えたいなどの、制約が無ければ useContext
を使用すると便利だと思います。