React学習 React文法

【React Hooks基礎】useContextを理解する

本記事ではReact Hooksの一つのuseContext()について詳細に解説いたします。

少しでも皆さんの理解につながれば嬉しいです。

useContext()の前にReact Contextの概念を理解する

useContext()はReact Hooksの一つで、Reactコンポーネント内のContextオブジェクトから簡単にデータを受け取るためのフックです。

しかしuseContext()の解説をする前に、まずはReact Contextの概念を理解する必要があります。

なぜならuseContext()はReact Contextの概念の中で使うものだからです。

React ContextとはReactコンポーネントツリー内で扱われるデータをグローバルに共有・管理するため概念です。

通常のデータの受け渡しは下図のようにpropsを使って親コンポーネントから子コンポーネントに渡されます。

例えば、App.jsにあるデータをLeftSub.jsとRightSub.jsで必要とすると一気にデータを渡すことができず、一度LeftSub.jsとRightSub.jsの各親コンポーネントを経由しなければなりません。

一方、React Contextの概念では下図のようにデータを扱います。

データをグローバルなものにして、データが必要なコンポーネントに直接共有できるようになります。

ReactContextはReactが持っている概念で、useContext()を使ってグローバルに共有されたデータに簡単にアクセスできるようになります。

何が嬉しいのかというと、規模の大きいアプリだとデータ管理が楽になります。

React Contextの実装方法

実際に実装しながらReact Contextを解説します。

完成図は下図で、「左の要素です」「右の要素です」というテキストデータをグローバルに扱えるようにして、App.jsの孫コンポーネントのLeftSub.js、RightSub.jsからそれぞれのテキストデータを取得できるように実装します。

実装上の注目ポイントはLeftSub.js、RightSub.jsの親コンポーネントであるLeft.js、Right.jsにはノータッチという点です。

ファイル構成はこちらです↓

コンポーネントの親子関係です↓

重要なのはstoreフォルダのdata-context.jsです。

中身にはこちらです。

import React from 'react';

const DataContext = React.createContext({
  textLeft: '',
  textRight: '',
});

export default DataContext;

createContext()でContextオブジェクトを生成します。

このContextオブジェクトがグローバルに共有されるようになります。

またここに設定するオブジェクトのデータはデフォルト値になります。

Contextオブジェクトを作ったあとは、他のコンポーネントがアクセスできるようにデータを供給します。
データを供給するにはProviderを使います。

App.jsに移動します。

本実装ではここにグローバルに扱いたいデータ元を準備します。

下記のソースのようにdata-context.jsに記載したDataContextをインポートし、JSXでDataContext.Providerでまるっと囲みます。

import React from 'react';
import Left from './components/Left';
import Right from './components/Right';
import DataContext from './store/data-context';  //←グローバルデータをインポート

function App() {
   const data = {
     textLeft: '左の要素です',
     textRight: '右の要素です',
   };
   return (
     <DataContext.Provider value={data}>  //←Providerで囲む
       <div className="container">
         <Left />
         <Right />
       </div>
     </DataContext.Provider>
   );
}

export default App;

こうすることによって、App.jsから見た子要素であるLeft.jsとRight.js、さらにLeft.jsとRight.jsの子要素であるLeftSub.jsとRightSub.jsのどこからでもグローバルのデータにアクセスできるようになります。

そしてグローバルに扱いたいデータを「value={〇〇}」という記述でこのようにProviderの中に追加します。<DataContext.Provider value={data}>と名前は必ず「value」でなければなりません。

今回はApp.jsに書かれている

const data = {
    textLeft: '左の要素です',
    textRight: '右の要素です',
  };

これをグローバルで扱うことにしています。

したがって<DataContext.Provider value={data}>という記述になります。

これでどのコンポーネントからでもデータにアクセスできるようになりました。

次からようやくuseContext()が出てきます。

useContext()でデータを取得する

冒頭に記述したように、useContext()を使うことでContextオブジェクトの値を簡単に取得できます。

データを読み取らせたいコンポーネントで下記の実装をします。

今回はLeftSub.jsを使います。

import React, { useContext } from 'react';
import DataContext from '../store/data-context';

const LeftSub = () => {
   const ctx = useContext(DataContext);
   return <p>{ctx.textLeft}</p>;
};

export default LeftSub;

一行目でuseContext()をインポートします。

二行目でContextオブジェクトをインポートします。

const ctx = useContext(DataContext);

この記述でctxにグローバルデータであるContextオブジェクトを格納しています。

そしてJSXでテキストデータを取得させています。

return <p>{ctx.textLeft}</p>;

一応RightSub.jsのソースもお見せします。ほぼ一緒です。

import React, { useContext } from 'react';
import DataContext from '../store/data-context';

const RightSub = () => {
   const ctx = useContext(DataContext);
   return <p>{ctx.textRight}</p>;
};

export default RightSub;

これで完成です。

何も触らなかったLeft.jsとRight.jsの記述も残しておきます。

import React from 'react';
import LeftSub from './LeftSub';

const Left = () => {
   return (
     <div className="left_container">
       <LeftSub />
     </div>
   );
};

export default Left;
import React from 'react';
import RightSub from './RightSub';

const Right = () => {
   return (
     <div className="right_container">
       <RightSub />
     </div>
   );
};

export default Right;

それぞれのコンポーネントでpropsを使っていないのと子コンポーネントにデータを渡していないのがポイントですね。

デフォルト値は意味があったのか?

序盤でdata-context.jsに記載したデフォルト値の設定ですが、

const DataContext = React.createContext({
  textLeft: '',
  textRight: '',
});

今回の実装ではApp.jsにグローバルにあつかうデータを用意したので、デフォルトのデータは正直あまり意味のない部分でした。

実際に下記のようにオブジェクト部分を消しても問題なく動きます。

const DataContext = React.createContext();

ではこのデフォルト値は何のために必要なのでしょう?

実はProviderを設定しなかったときにこのデフォルトの値を読み取ることになります。
しかしProviderを設定しない実装は現実的にあまりないです。

もう一つは些細なことですが、デフォルト値を設定していることでエディターが推測してくれます。(エディターの機能にもよります)

このようにデフォルト値(右)のtextLeftをコメントアウトするとコンポーネントで「ctx.」と打つと候補としてtextRightだけしか出てこなくなります。

React Contextを使わなかった時の実装と比較

最後にReact Contextを使なかった時の実装を見てみましょう。

特に注目して欲しいのが、次の2点です

  • データをパスするためだけの余計な記述
  • propsを使ってのデータの受け渡しの発生

React Contextを使わなかった場合の実装はこちら。

function App() {
  const data = {
    textLeft: '左の要素です',
    textRight: '右の要素です',
  };
  return (
    <div className="container">
      <Left textData={data.textLeft} />
      <Right textData={data.textRight} />
    </div>
  );
}
const Left = (props) => {
  return (
    <div className="left_container">
      <LeftSub text={props.textData} />
    </div>
  );
};
const LeftSub = (props) => {
  return <p>{props.text}</p>;
};

React Contextを使わなければApp.js→Left.js →LeftSub.jsという流れになり、データを使わないLeft.jsにただデータをパスしなければなりませんでした。

またApp.js以降のコンポーネントでpropsでのデータの受け渡しが発生しています。

これ自体に問題はないのですが、React ContextとuseContext()を理解していればより綺麗に記述することができます。

最後に

今回はuseContext()の解説をしました。

useContext()はReact Contextの理解があって初めて使えます。

序盤に図解でわかりやすくなっているともので、まずはReact Contetの理解から進めましょう。

少しでもこれからプログラミングを学ぼうと考えている方の為になる発信をできれば思います。

私がReactを学んでいるUdemyの講座が下記です。

この私のReactの全記事も下記の講座をより理解しやすく解説したものです。講座を受けつつ、不明点をこのブログで解消できるような構成になっています。

48時間と長いですが基本から応用までかなり解説してくれているので興味があればぜひチェックしてみてください😌

ベストセラー取得

-React学習, React文法