ReactのContext APIはReduxの代わりとなるのか


Reactのv16.3から新しくContextAPIが正式リリースされました。
ContextAPIは、Reduxのようなstateの一元管理を行うStoreのようなふるまいができる機能です。
今回の記事のタイトルにある"ContextAPIがReduxの代わりとなるか"について、結論を先に言うとReduxの代わりにはなりません。あくまでReduxでのStore部分のみの機能であり、Reducerなどのfunctionは提供されていません。(Reduxについて詳しく知りたい方はこちらをどうぞ)

この記事では、公式のドキュメントを参考に、Contextで何が出来るのか、Context APIの使い方を紹介します。

Contextが作られた背景

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Reactアプリケーションでは、親子になっているコンポーネント間で値を渡す場合は、propsを経由して渡していました。しかし、コンポーネントのツリーが大きくなりネストが深くなるとpropsでのバケツリレーが面倒になってきます。それを解決するために、Contextを使うとどの階層のコンポーネントからでもContextが保持しているデータにアクセスすることができます。

Contextをいつ使うか

公式が提唱するContextの利用シーンは、「グローバル」な値として多くのコンポーネントから参照されるデータに適しています。
例えば、

  • ログイン中のユーザー情報
  • UIテーマ
  • 言語設定

などです。

Contextの使用方法

Contextが提供するAPIで登場するのは主に三つです。

  • React.createContext
  • Provider
  • Consumer

簡単に説明すると、ProviderでContextにvalueを設定し、ConsumerでContextに設定されている値を取り出します。

createContext

const {Provider, Consumer} = React.createContext(defaultValue);

ProviderとConsumerのペアを作成します。
defaultValueにはContextへ設定するvalueの初期値を設定できます。

Provider

<Provider value={/* some value */}>

valueに設定した値をContextに設定します。
Providerで設定した値は、Provider内のConsumerへ渡されます。
valueが変更された際には、変更を検知して再描画されます。

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

親のProviderが設定したvalueを取り出します。
Consumerの中はfunctionである必要がありfunctionの引数にContextから取得したvalueが渡されます。

Providerはネストすることも可能です。Consumerは親のProviderコンポーネントの中で一番近いProviderのcontext valueを取得します。また、Consumerは親にProviderを持たなくても大丈夫です。その場合はcreateContextで設定したdefaultValueを取得します。

個人的な見解

Reduxを使うまでにいかない小規模のアプリケーションで、propsでのバケツリレーがしんどくなるケースはよくあります。そんなときにこのContextを使えば楽になってシンプルになりそうという印象です。
ただ、Contextのvalueを下層コンポーネントから取り出しやすいけれど、値をセットするのは親のProviderで行わないといけず、下層コンポーネントからだと、結局Providerの値変更をするfunctionをpropsでバケツリレーする感じが、痒いところに手が届かない状態。(reduxのようにどこからでもdispatchできない)
なので、Context valueを変更する機会は少ないようなシチュエーション、上記で挙げたグローバルな設定値とかで使うのに留めておくのが良さそうという感想です。

Contextを使い際のTips的なものも公式のドキュメントに載っているので、利用する方は一読するのをオススメします。