概要
Reactでコンポーネントがデータを扱う方法にはProps
、State
そしてContext
があります。今回のブログポストではContextに関する概念と使い方を説明します。
ReactでPropsとStateの概念と使い方については下記のリンクを確認してください。
ここで紹介するスースコードは下記のリンクで確認できます。
- GitHub: context_example
Context
ReactでPropsとStateは親コンポーネントと子コンポーネント、または1つのコンポーネント中でデータを扱う時使います。このPropsとStateを使う場合、親コンポーネントから子コンポーネント、つまり上から下、片側にデータが流れるようになります。
もし、他のコンポーネントで片側で流れてるデータを使いたい場合、または他のコンポーネントが使ってるデータを現在のデータ流れに入れたい場合はどうすればいいでしょうか?
Reactではデータは上から下に流れるので、使いたいデータとこのデータを使うコンポーネントの共通親コンポーネントにStateを作って使いたいデータをPropsで子コンポーネントに渡せば問題を解決することができます。
しかし、このようにコンポーネント中で共有されるデータのため毎回共通親コンポーネントを修正して全ての子コンポーネントにデータをPropsで渡すことは非常に非効率です。このような問題を解決するためReactではFlux
と言う概念を導入し、それに合うContext API
を提供を始めました。
- Reactの公式サイト: Context
Contextは親コンポーネントから子コンポーネントに渡されるデータの流れとは関係なくグローバル的なデータを扱う時使います。グローバルデータをContextに保存した後、データが必要なコンポーネントで当該データを取ってきて使います。
ReactでContextを使うためにはContext APIを使う必要があり、ContextのProvider
とConsumer
を使う必要があります。
Contextで保存されたデータを使うためには共通親コンポーネントにContextのProviderを使ってデータを提供いて、データを使うコンポーネントではContextのConsumerを使って実際データを使います。
Contextの使い方
そしたら今からContext APIを使ってReactでグローバルデータを扱う方法について説明します。
プロジェクト準備
次のコマンドを実行してContextを使うためReactプロジェクトを生成します。
npx create-react-app context_example --template=typescript
Contextの生成
そして、グローバルデータを保存するためContextを生成してみましょう。./src/Contexts/Count/index.tsx
ファイルを生成して下記のように修正します。
import { createContext, useState } from 'react';
const CountContext = createContext({
count: 0,
plusCount: () => {},
});
interface Props {
children: JSX.Element | JSX.Element[];
}
const CountProvider = ({ children }: Props): JSX.Element => {
const [count, setCount] = useState(0);
const plusCount = (): void => {
setCount(count + 1);
};
return (
<CountContext.Provider
value={{
count,
plusCount,
}}>
{children}
</CountContext.Provider>
);
};
export { CountContext, CountProvider };
ReactでContextを生成するためにはcreateContextを使います。また、Contextも1つのReactコンポーネントなので、コンポーネント中で変更可能なデータを扱うためStateを使う必要があります。
import { createContext, useState } from 'react';
このように追加したcreateContextを使ってContextを生成します。この時、グローバルで使えるデータの初期値を設定する必要があります。
const CountContext = createContext({
count: 0,
plusCount: () => {},
});
Contextも1つのReactコンポーネントなので、基本的にはコンポーネントの形をしてます。この時、画面に表示される内容をContextのProviderを包んで提供します。
...
const CountProvider = ({ children }: Props): JSX.Element => {
...
return (
<CountContext.Provider>
{children}
</CountContext.Provider>
);
};
export { CountContext, CountProvider };
Contextは1つのReactコンポーネントです。したがって、内部的に変更可能なデータを使うためにはuseStateを使ってStateを使う必要があります。
const CountProvider = ({ children }: Props): JSX.Element => {
const [count, setCount] = useState(0);
const plusCount = (): void => {
setCount(count + 1);
};
...
};
このように作ったStateをContextのProviderに提供します。
const CountProvider = ({ children }: Props): JSX.Element => {
...
return (
<CountContext.Provider
value={{
count,
plusCount,
}}>
{children}
</CountContext.Provider>
);
};
最後にcreateContextを使って生成したContextとContextのProviderを使って作ったReactコンポーネントをエクスポートします。ContextのProviderを使って作ったReactコンポーネントは共通親コンポーネントに提供する予定で、createContextで生成したContextはデータを消費する時、使う予定です。
export { CountContext, CountProvider };
これでグローバルデータを扱うためContextを生成してみました。これからこのContextを使う方法について説明します。
Provider
上で作ったContextを使うため、共通親コンポーネントであるApp
コンポーネントにContextのProviderを提供してみます。ContextのProviderを提供するため、./src/App.tsx
ファイルを開いて下記のように修正します。
import { CountProvider } from './Contexts/Count';
import { CountLabel } from './Components/CountLabel';
import { PlusButton } from './Components/PlusButton';
function App() {
return (
<CountProvider>
<CountLabel />
<PlusButton />
</CountProvider>
);
}
export default App;
Contextを使ってグローバルデータを使うためには共通親コンポーネントでContextのProviderを使う必要があります。ここで使うと言う意味はContextのProviderで包むことを意味します。
import { CountProvider } from './Contexts/Count';
...
function App() {
return (
<CountProvider>
...
</CountProvider>
);
}
export default App;
このようにCountProvider
で包んだ部分ではContext内部で生成したグローバルデータを自由にアクセスすることができます。
...
import { CountLabel } from './Components/CountLabel';
import { PlusButton } from './Components/PlusButton';
function App() {
return (
<CountProvider>
<CountLabel />
<PlusButton />
</CountProvider>
);
}
またContextを使うコンポーネントを作ってないのでエラーが発生してます。今からContextを使うCountLabel
コンポーネントとPlusButton
コンポーネントを作ってみましょう。
Consumer
Contextを使ってグローバルデータを使うコンポーネントを作成してみましょう。まず、./src/Components/CountLabel/index.tsx
ファイルを生成して次のように修正します。
import { useContext } from 'react';
import { CountContext } from '../../Contexts/Count';
export const CountLabel = () => {
const { count } = useContext(CountContext);
return <div>{count}</div>;
};
CountLabelは単純にカウントを画面に表示するコンポーネントです。ここで表示するカウントはグローバル変数で、Contextに宣言した値を使う予定です。このようにContextに宣言された値を使う時、ReactのクラスコンポーネントではConsumer
を使いますが、関数コンポーネントではuseContext
フック(Hook)を使います。
useContextフックを使って私たちが作ったContextであるCountContextをパラメーターで渡すと、createContextで生成した値にアクセスすることができます。
const CountContext = createContext({
count: 0,
plusCount: () => {},
});
CountLabelではContextのcount変数だけ使います。
...
export const CountLabel = () => {
const { count } = useContext(CountContext);
return <div>{count}</div>;
};
次はContextのcount変数の値を上げるPlusButton
コンポーネントを生成してみましょう。./src/Components/PlusButton/index.tsx
ファイルを生成して次のように修正します。
import { useContext } from 'react';
import { CountContext } from '../../Contexts/Count';
export const PlusButton = () => {
const { plusCount } = useContext(CountContext);
return <button onClick={plusCount}>+ 1</button>;
};
PlusButtonは画面に単純に+ 1
と言うボタンを表示して、そのボタンを押した時、Contextのcount値を上げる関数であるplusCount
関数をコールするコンポーネントです。
CountLabelと同じようにuseContextフックと私たちが作ったContextを使って、グローバルデータであるcountの値を上げるplusCount
関数を取ってきました。このように取ってきた関数をボタンのonClick
に連結しました。
確認
Contextを使ってグローバルデータを扱う例題を作ってみました。このように作ったReactプロジェクトを確認するため、次のコマンドを使ってReactプロジェクトを実行します。
npm start
Reactプロジェクトが実行されたら次のように0
と+ 1
ボタンが表示されることが確認できます。
+ 1
ボタンを押すと、次のようにカウントが上がることが確認できます。
完了
これでReactでコンポーネントがグローバルデータを扱う方法であるContextについて簡単に説明しました。今回のブログポストの例題ではContextを使わなくてもいいぐらいの小さいプロジェクトだったので、Contextを使う必要性を感じてなかったと思います。しかし、たくさんのコンポーネントを使ってるプロジェクトではContextを使ってデータを共有する場合がありますので、今回の機会でよく勉強して置くと役に立つと思います。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Deku
が開発したアプリを使ってみてください。Deku
が開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。