はじめに
こんにちは!
初心者プログラマーのジンです!
今日も頑張って知識を深めます!
今回も宿敵ReactのHooksと闘います。
昨日の敵は何とやら、、、。
前回記事にしたuseStateとuseEffectの記事はこちら!
https://qiita.com/jingle07/items/3c472276592c8f1b5545
アウトプットの難しさはもちろんありますが、
人に伝える上でそれ以上の理解もできて、
なんか得している気分です!
鉄は熱いうちに叩け!ということで
第2弾開幕です。
useContext
コンテキストAPIと連携して、コンポーネントツリーの深い階層にあるデータを直接取得するためのフック。
できることの一例
・ログインユーザー情報の保持
・ダークモードとライトモード切替?
・表示言語の選択
こんちくしょーAPI?知らんけど
モードの切り替えとかuseStateでいいじゃん!
以前までの私はこう調子に乗ってました。
コンテキストAPIとは?
調べると、
コンテキストAPIは、Reactでコンポーネント間においてデータを共有するための仕組みです。通常、Reactでは親から子へとプロップス(props)を使ってデータを渡しますが、コンポーネント階層が深くなると、複数の中間コンポーネントを経由してデータを渡さなければならないことがあります。これを「プロップスドリリング」と呼びますが、コンテキストAPIを使うと、この問題を解決できます。
とあります。
まずは意味から理解したいと思います。
コンテキスト(Context)は「文脈」、「状況」という意味でした。
APIについてですが、
「APIを叩く」のような言葉をよく耳にしますが、
いざAPIって何ですか?
と聞かれても説明できないなと思ったので以下に調べました。
APIの和訳は
「アプリケーションプログラミングインターフェース」です。
この言葉を分解すると、次のような意味になります:
- アプリケーション:ソフトウェアやプログラムのこと
- プログラミング:プログラムを書くこと
- インターフェース:異なるもの同士がやり取りをするための仕組みや手段
まとめると、APIは「アプリケーション同士がやり取りをするための仕組み」という意味になります。
今までの筆者の認識としては
APIは調べたいデータを引っぱり出してくる保管場所のようなイメージでした。
意味合い自体は似ているようですがもっと幅を大きく、
全体の仕組み自体のことを総じてAPIというんだなと改めて勉強になりました。
つなげて直訳すると
「文脈や状況をやり取りするための仕組み」となります。
結局何ができるの?
意味を理解したところで本題です。
結局どんな利点があるのか?
利点として大きく上げられるのは
・プロップスの受け渡しが必要ない
・無駄が省けるしエラーも起こりにくくなる
・全体で共通に使用できる
言葉ではいまいち伝わりにくいと思うので、イラストをChatGPT先生にお願いしました。
※通常親コンポーネントから4階層下の子コンポーネントにプロップスを渡す場合
通常
(下の→は無視してください)
Reactではデータに関してはプロップスとして中間コンポーネントも介して届けなければなりません。
それがコンテキストAPIを使うと
こんなイメージかなと思います。
どの階層にいても各々が自分で正しくコンテキストデータを受け取ることができます。
コンテキストAPIの実際の仕組み
-
React.createContext()
: コンテキストを作成します。これにより、プロバイダーとコンシューマーが生成されます。
const MyContext = React.createContext();
- プロバイダー(Provider): コンテキストを提供するコンポーネントです。プロバイダーに渡した値が、ツリー内のすべてのコンシューマーコンポーネントで利用できます。
<MyContext.Provider value={/* 共有したいデータ */}>
{/* 子コンポーネント */}
</MyContext.Provider>
- コンシューマー(Consumer): プロバイダーから渡されたコンテキストの値を取得するコンポーネントです。※ここでuseContextを使用!
const value = useContext(MyContext);
コードで見る比較
4階層のコンポーネントでPropsを親コンポーネントから一番下の子コンポーネントまで渡していくとき。
例:4階層コンポーネントでのProps受け渡し
親コンポーネント(Parent)
第1の中間コンポーネント(Middle1)
第2の中間コンポーネント(Middle2)
子コンポーネント(Child)
import React from 'react';
// 子コンポーネント(Child)
const Child = ({ message }) => {
return <div>子コンポーネント: {message}</div>;
};
// 第2の中間コンポーネント(Middle2)
const Middle2 = ({ message }) => {
return (
<div>
第2の中間コンポーネント:
<Child message={message} /> {/* Propsを子コンポーネントに渡す */}
</div>
);
};
// 第1の中間コンポーネント(Middle1)
const Middle1 = ({ message }) => {
return (
<div>
第1の中間コンポーネント:
<Middle2 message={message} /> {/* Propsを第2の中間コンポーネントに渡す */}
</div>
);
};
// 親コンポーネント(Parent)
const Parent = () => {
const message = "こんにちは、これは親からのメッセージです!";
return (
<div>
親コンポーネント:
<Middle1 message={message} /> {/* Propsを第1の中間コンポーネントに渡す */}
</div>
);
};
export default Parent;
実行結果:
親コンポーネント:
第1の中間コンポーネント:
第2の中間コンポーネント:
子コンポーネント: こんにちは、これは親からのメッセージです!
流れ
Parentコンポーネントでmessageという文字列を定義しています。
Middle1コンポーネントにそのmessageを渡します。
Middle2コンポーネントでは、messageを受け取り、さらにChildコンポーネントに渡します。
最終的に、Childコンポーネントでmessageを受け取り、表示します。
4階層のコンポーネントでuseContextを使った場合
親コンポーネント(Parent)
第1の中間コンポーネント(Middle1)
第2の中間コンポーネント(Middle2)
子コンポーネント(Child)
useContextを使うと、各コンポーネントで直接コンテキストにアクセスできるため、中間コンポーネントはPropsを渡す必要がなくなります。
import React, { createContext, useContext } from 'react';
// 1. コンテキストを作成
const MyContext = createContext();
// 子コンポーネント(Child)
const Child = () => {
const message = useContext(MyContext); // 4. useContextでコンテキストの値を取得
return <div>子コンポーネント: {message}</div>;
};
// 第2の中間コンポーネント(Middle2)
const Middle2 = () => {
return (
<div>
第2の中間コンポーネント:
<Child /> {/* Propsを渡さない */}
</div>
);
};
// 第1の中間コンポーネント(Middle1)
const Middle1 = () => {
return (
<div>
第1の中間コンポーネント:
<Middle2 /> {/* Propsを渡さない */}
</div>
);
};
// 親コンポーネント(Parent)
const Parent = () => {
const message = "こんにちは、これは親からのメッセージです!";
return (
// 2. Providerでコンテキストの値を提供
<MyContext.Provider value={message}>
<div>
親コンポーネント:
<Middle1 /> {/* Propsを渡さない */}
</div>
</MyContext.Provider>
);
};
export default Parent;
実行結果:
親コンポーネント:
第1の中間コンポーネント:
第2の中間コンポーネント:
子コンポーネント: こんにちは、これは親からのメッセージです!
通常では
各階層のコンポーネントで、親コンポーネントからデータを直接受け取る子コンポーネントにたどり着くまで、複数の中間コンポーネントでPropsを受け取り、次に渡すという手間がありました。プロップスドリリングというやつですね。
しかしuseContextを使うことで、各コンポーネントで直接コンテキストにアクセスでき、Propsを中間コンポーネントで渡す必要がなくなりました。
MyContext.Providerを使って、親コンポーネントから子孫コンポーネントに向かって値を共有します。この例では、ChildコンポーネントがuseContextを使って直接messageにアクセスしています。
冒頭の説明文で書いていた、「通常、Reactでは親から子へとプロップス(props)を使ってデータを渡しますが、コンポーネント階層が深くなると、複数の中間コンポーネントを経由してデータを渡さなければならないことがあります。これを「プロップスドリリング」と呼びますが、コンテキストAPIを使うと、この問題を解決できます。」
の意味を比較を通して理解することができました。
最後に
調べる前、
ダークモードとライトモード切替?
モードの切り替えとかuseStateでいいじゃん!と言っていた筆者の甘い甘い考えは、
都度プロップスを渡したり、コード書きながら引き継がないといけないので、
できないことはないけど、めちゃくちゃ管理面倒くさいことしなくてはならない、、、。
と反省させられました、、、。
今回改めて勉強して、1つのフックの説明にフック以外の多くの基礎知識が改めて勉強になりました。
本当はもう2、3個書きたかったですが、次回にします。
これを続けて少しでも書けるようになりたい!!