Contextとは
Reactにはローカルでデータを一時的に保管できる場所として、stateと呼ばれるものがあるかと思います。しかしプロジェクトが大きくなったり、小さいプロジェクトでも複数のコンポーネントで同じデータ(state)を扱いたいとき、stateをpropsを用いて各コンポーネントに配送する作業をせねばなりません。このデータの配送作業の面倒な部分は、データを使わないコンポーネントにも場合によっては中継する必要があり、コード量が増えてしまうことになります。(自分を困惑させるようなコードが増えます)
ここで登場してのがContextAPIと呼ばれるものです。このCotextAPIというものを用いれば従来のstateではなく、contextというデータ保管庫を全てのコンポーネントの中央に置くことができます。このcontextはイメージとしてはハブ空港のようなもので、あるデータを使いたいコンポーネントは親コンポーネントを経由することなくcontext(データ保管庫)から直接データを受け取れます。使ったことある方も多いと思いますが、Reduxと考え方、動きは非常に似ています。
ContextとProviderを作成する
まずはsrcディレクトリの直下にcontextを作成するファイルを入れるcontextフォルダを作ります。(ディレクトリを作成する必要はありませんが、分かりやすくするためです。)その中に実際にcontextを作るファイルを作成します。今回は画面の配色データを保管するcontextを作成するため、ファイル名はThemeContext.jsとしました。
下のコードにある通り、まずはcreateContext関数を用いてcontextを作成します。ただこれだけではデータを保管する部分、コンポーネントにデータを届ける(provide)機能は設定しておらず。何も動きません。実際にデータを保管する場所、データを配送する機能はProviderと呼ばれるコンポーネントを作成し、その中で設定します。最初に自分の使いたいデータの保管部分を作成しましょう。少し驚くかと思いますがデータを保管するのはstateです。stateオブジェクトに自分の使いたいデータを入れましょう。
import React, { createContext } from 'react';
// createContextはcontextを作成してくれる関数
export const ThemeContext = createContext();
// contextを作成し、外部でも使えるようエクスポートしておく
class ThemeContextProvider extends React.Component {
state = {
isLightTheme: true,
light: {
syntax: '#555',
ui: '#ddd',
bg: '#eee'
},
dark: {
syntax: '#ddd',
ui: '#333',
bg: '#555'
}
}
render() {
return (
// 動的な値であるため{}で囲み、stateのコピーを入れるために{}を書く
// 従って2重の{}が必要となる。
<ThemeContext.Provider value={{...this.state}}>
{this.props.children}
</ThemeContext.Provider>
);
}
}
export default ThemeContextProvider;
次にProviderを作成します。Providerとは簡単に言えばタグです。contextはデータを各コンポーネントに送る際に、そのコンポーネントをタグ(Provider)で囲むことで、コンポーネントに対してデータを送り、操作できる機能も与えます。タグというだけあってrender関数の中のJSXとして記述します。上のコードのようにThemeContext.Providerと書きます。createContext関数を用いて作られたcontextにはProviderが付与されるためです。このProviderがコンポーネントを囲むことでデータを送ることができ、コンポーネントはデータを扱うことができます。Providerにはvalueプロパティを設定しなければなりません。valueプロパティの値には送りたいデータを記述します。従ってスプレッド構文を用いてstateの中身をコピーし、値とします。これによりProviderによって囲まれたコンポーネントはvalueプロパティ内のデータをを使うことができます。ただProvider側は囲まれたコンポーネントをどのようにして認識するのか。それが**{ this.props.children }の部分です。
では上コードの{ this.props.children }**とは何か。this.props.childrenは子コンポーネントを表します。下のコードのようにコンポーネント達がcontextを使いたい場合、App.jsでProviderに囲んでもらいます。囲まれたコンポーネントはProvider側から見れば子コンポーネントとなり、このように書くことで、Providerは囲まれたコンポーネント全てを認識できます。
import React from 'react';
import Navbar from './components/Navbar';
import Todo from './components/Todo';
import ThemeContextProvider from './context/ThemeContext';
function App() {
return (
<div className="App">
<ThemeContextProvider>
<Navbar />
<Todo />
</ThemeContextProvider>
</div>
);
}
export default App;
Contextのデータを使ってみる(クラスベース)
Providerによりコンポーネントを囲んだら、そのコンポーネントに移動し、まずcontextをインポートします。あとは下のコードにある通りcontextTypeに自分の使いたいcontextを代入すれば、this.contextでコンポーネントからcontextにアクセスできるようになります。
import React, { Component } from 'react';
import { ThemeContext } from '../context/ThemeContext';
class Navabar extends Component {
// これによりthis.contextで、contextにアクセスできる
static contextType = ThemeContext;
render() {
console.log(this.context);
return (
<nav>
<h1>Context App</h1>
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
)
}
}
export default Navabar;
最後に
読んでくれた方はありがとうございます。間違い、誤字などございましたら、お気軽に指摘していただけると助かります。