投稿日:2021年2月10日
ContextはReactのv16.3から利用できるようになった機能で、これを利用することであらゆるコンポーネントから特定のObjectにアクセスできるようになります。 この記事ではContextの実例とReduxとの使い分けについて解説しています。
この記事は以下の環境を前提にしています。
まずはじめにコンテキストを作成する必要があります。
コンテキストの作成はcreateContext()メソッドを使います。
import React from 'react';
export const SampleTextContext = React.createContext("aaaaa");
createContext()の引数はContextの初期値です。
今回は適当にaaaaaという文字列にしました。
次に値を利用したいコンポーネント側の実装を見ていきましょう!
関数コンポーネントで値を取得する場合には、コンテキストのConsumerタグを使います。
import React from "react";
import { SampleTextContext } from "../contexts";
const MyFunctionalComponents = () => {
return (
<SampleTextContext.Consumer>
{value => {
return(
<div>{value}</div>
)
}}
</SampleTextContext.Consumer>
)
}
export default MyFunctionalComponents;
Consumerタグのchildren要素はコンテキストの値を受け取ってReactノードを返す関数である必要があります。
valueの値が変わると自動的に再レンダリングされます。
次にクラスコンポーネントでコンテキストの値を利用する場合について説明します。
まずは前準備として静的フィールドのcontextTypeを利用したいコンテキストに設定します。
これによってクラスコンポーネントはcontextフィールドに対象のコンテキストの値をサブスクライブするようになります。
import React from "react";
import {SampleTextContext} from "../contexts";
export default class MyClassComponents extends React.Component<{},{text:string}>{
static contextType = SampleTextContext;
render(){
return (
<div>
{this.context}
</div>
)
}
}
コンポーネントがContextの値を読み取るには、そのコンポーネントの階層関係の親要素にあたるどこかでContextのProviderタグを呼び出す必要があります。
ここでは、上で作成した2つのコンポーネントをProviderで包んだ上で、更にContextの値を変更するボタンを作成してみます。
import React from 'react';
import MyClassComponent from "./components/MyClassComponents";
import MyFunctionalComponent from "./components/MyFunctionalComponent";
import {SampleTextContext} from "./contexts";
function App() {
const [sampleText, setSampleText] = React.useState("aaaa");
return (
<SampleTextContext.Provider value={sampleText}>
<button onClick={()=>{setSampleText(sampleText + "a")}}>ボタン</button>
<div className="App">
<MyClassComponent />
<MyFunctionalComponent />
</div>
</SampleTextContext.Provider>
);
}
export default App;
ボタンをクリックする度にSampleTextContextの値が変更されます。
子要素のMyClassComponentとMyFunctionalComponentは値の変更をサブスクライブし、再レンダリングされることを確認できると思います。
ここでContextのメリット、デメリットからReduxとの使い分けを考えてみます。
Reduxと比較してContextは状態の導入と管理が非常に簡単です。
特に追加のパッケージなどを利用せずとも、基本的に以下の3手順で利用することができます。
Reduxと違い、Contextで値を利用した際にはコンポーネントのすべてのDOM要素が再レンダリングされます。
Contextの値が頻繁に変更される場合と、ContextのProvider内のコンポーネントの範囲が広い場合、再レンダリングのコストは非常に大きくなってしまいます。
複数のContextを同一箇所で利用しようとした場合に、おそらく頭を悩ませることになると思います。
まず関数コンポーネントの場合以下のように無駄にネストが深くなってしまいます。
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
またクラスコンポーネントの場合にはさらに問題は大きく、
通常の方法では複数Contextの利用すらできません。
以上のContextのメリット、デメリットから
以下のような場合にContextを使用するのがいいのではないでしょうか?