Reactの進化に合わせて、Contextの使いやすさも上がってきている、と感じました。
Context
とは
Context
は、React 16.3で正式導入された1仕組みで、props
を経由せずに親から子孫へ値を渡せる仕組みです。
使い方(渡す側)
まずは、const SomeContext = React.createContext(デフォルト値);
のようにして、コンテキストを作成します。そして、コンテキストへ値を供給したい側では、<SomeContext.Provider value={値}>...</SomeContext.Provider>
で囲むことで、これに囲まれた子孫コンポーネントから値
を取り出し可能となります。
使い方(使う側)
このコンテキスト値を取り出そうとする場合、取り方が3通りあります。
Context.Consumer
(React >= 16.3)
コンテキストの正式導入と同時に入った方法がContext.Consumer
で、これはchildren
としてRender prop的な関数を取って、その関数に値が入ってくる、という流れになっています。
<SomeContext.Consumer>
{
value => (
<SomeChildren value={value} />
)
}
</SomeContext.Consumer>
Component.contextType
(React >= 16.6)
「クラスコンポーネントにRender propは合わせづらい」ということもあってか、React 16.6ではクラスコンポーネントでコンテキストを使う機能が導入されました。詳しくは以前に書きましたが、大きな制約として1つしか設定できないということがあります。
React.useContext
(React >= 16.8)
そして、React Hooksの一つとしてもコンテキストが使えるようになっています。見た目的にはconst value = React.useContext(SomeContext);
と、単なる関数であるかのように書けるのですが、実態としては別物なので、使うに当たってルールがあります。
コンテキストの動作とメリット・デメリット
コンテキストを使うメリットとしては、まず「propsを介さず、目的の場所で必要な値を使える」ということがありますが、もう1つのメリットとして「Reactの管理内にある」ということがあります。React外で値を共有してしまうと、値を更新した際にReactの更新がうまくついていかないことが考えられますが、コンテキストはReactの管理内なので、もちろんそんな心配はありません。
一方で、コンテキストが書き換わればコンポーネントが全面的に書き換わるので、クラスコンポーネントのstate
のような、「大きな構造を持っていて、その一部分だけが書き換わることが多い」オブジェクトを入れてしまうと、不必要な更新が増える危険があります。また、かつては「Render propが読み書きしづらい」という問題もあったのですが、React 16.8ではクラスコンポーネントで複数のコンテキストが必要になる場合を除いて問題は解消しています。
便利な利用例
全体に及ぶ環境設定
言語・ユーザーなど、アプリケーション全体に影響が及ぶ環境をハンドリングするのに、コンテキストはぴったりです。これらは子孫のどこで使うか把握するのも難しいですし、更新する機会も(プログラムの動作というスケールで考えれば)ほとんど来ないので、更新時の弊害もあまりありません。
stateの更新用関数の引き回し
React Hooksを使った場合、コンテキストの取り出しがごく簡単に行なえます。ということで、setStateValue
のような、stateの更新用関数をコンテキストで引き回して下位から使う、ということの実用性がかなり上がっています。さらに、React Hooksでは複数のstate
がバラバラに存在する状態となるので、useState
で生成したセット用の関数をそのまま流用することもできてしまいます。
-
それ以前にも別なコンテキストAPIがありましたが、本稿では扱いません。 ↩