Context とは
React の Context(コンテクスト) は、いわゆるグローバル変数の格納領域のようなもので、複数のコンポーネント間でのデータ共有に使用できます。 各コンポーネントから Context 情報にダイレクトにアクセスできるため、prop のように上位のコンポーネントから情報を伝搬させていく必要がありません。
Context をむやみに使うと、コンポーネントの再利用性が下がってしまいますが、アプリケーション全体で使用する次のような情報を Context で管理するとコードがすっきりします。
- ログイン中のユーザー情報(認証情報)
- 言語設定
- テーマ設定
Context の使い方
Context を作成する (createContext)
Context オブジェクトは、React.createContext()
で作成することができます。
複数のコンポーネントから参照することになるので、Context オブジェクトは単独のファイルとしてモジュール化しておきます。
React.createContext()
の引数に任意のオブジェクトを渡すと、その値を初期値とする Context
オブジェクトが生成されます。
次の例では、string
型のデータを保持する Context
オブジェクトを生成しています。
Context を参照する (useContext)
関数コンポーネント内で、Context が保持するデータを参照するには、React.useContext()
を使用します。
デフォルトでは、React.createContext()
の引数に設定したデフォルト値が返されます。
上記の例では、Default message
というデフォルトテキストを設定しているので、その値が返されることになります。
このように、どの階層にあるコンポーネントからも、React.useContext()
を使って Context が保持する情報を参照することができます。
しかし、このままだと、Context が保持する値を変更することができません。
Context の値を更新してコンポーネントを再描画する
前述の React.createContext()
で作成した MyContext
には、MyContext.Provider
というコンポーネントが付随しています。
このコンポーネントは、次のように value
属性と一緒に使用します。
このようにすると、子コンポーネント内で React.useContext()
を呼び出したときに返される値が、上記の value
属性で指定した値に変化します。
つまり、この value
属性の値を、ステートオブジェクトを使って設定することで、子コンポーネントを新しい Context データで再描画できるようになります。
次の例では、MyContext.Provider
の value
属性に、React.useState()
で作成した message
ステートオブジェクトを設定しています。
message
ステートオブジェクトの初期値は、React.useContext()
が返すデフォルト値 (Default message
) に設定しています。
この message
ステートオブジェクトの値を setMessage
関数で変更することにより、MyContext.Provider
以下のコンポーネントに再描画がかかります。
Child
コンポーネントは、次のように MyContext
の内容を表示しているだけです。
Change ボタンを押すと、Child
コンポーネントに表示されるメッセージが Default message
から New message
に変化します。
どのコンポーネントからでも Context の値を更新できるようにする
ここまで見てきたように、Context
データの変化を子コンポーネントに伝えるには、Context.Provider
コンポーネントの value
属性の値をうまく更新してやる必要があります。
このような更新処理は、Context.Provider
を配置しているコンポーネント内では容易に行えますが(前述の例)、任意の階層のコンポーネントから Context
データを更新できるようにする には少々工夫が必要です。
ここでは、Context
データとして、自身のデータを更新するためのセッター関数を持つようにする例を示します。
下記は、作成するアプリの表示例です。
UserInfo
コンポーネントは Context
データの内容を表示し、UpdateButtons
コンポーネントは Context
データの内容を変更するボタンを表示します。
例えば、Change username
ボタンを押すと、Context
が保持する username
の値が変化し、画面上の表示も更新されます。
UserInfo
と UpdateButtons
は prop を介した親子構造により連携しているわけではなく、Context
によってのみ連携しています。
AppContext コンポーネントの作成
ここで定義する AppContext
は、アプリ全体で共有する 2 つの文字列データ(username
と apiToken
)を持ち、さらにそれらの値を更新するためのセッター関数 (setUsername
と setApiToken
) を持ちます。
ここでのポイントは、任意の子コンポーネントを AppContext.Provider
以下に配置するための AppContextProvider
を定義しているところです。
ここで、React.useState()
で作成したステートオブジェクトを使って AppContext
データを再構成することで、各種セッター関数が正しく初期化されます。
子コンポーネント ({children}
) からセッター関数を呼ぶことにより、AppContextProvider
コンポーネントの再描画が走るため、結果的に、関連するすべての子コンポーネントが再描画されることになります。
UserInfo コンポーネントの作成
UserInfo
コンポーネントは、AppContext
が保持するユーザーデータを単純に表示します。
ここでは React.useContext()
をそのまま使っていますが、useAppContext()
のようなカスタムフックを作成すれば、よりスッキリしたコードになります。
UpdateButtons コンポーネントの作成
UpdateButtons
コンポーネントは、AppContext
が保持するデータを変更するためのボタン(Change username
と Change apiToken
)を配置します。
AppContext
の内容を更新したいときは、AppContext
自身が提供するセッター関数を呼び出すだけで済みます。
App コンポーネントの作成
最後に、最上位のコンポーネントである App
コンポーネントです。
AppContextProvider
の子コンポーネントとして配置された UserInfo
や UpdateButtons
からは、AppContext
が保持するデータを参照・変更することができます。