LoginSignup
2
0

useContextを使うべく、公式追っていく。

Posted at

今回はuseContextを見ていきます。
公式のreferenceでもいいのですが、今回はコンテクストで深くデータを受け渡すを見ていこうと思います。
(公式を追いながら、感じたことを含めてアウトプットしています。コードは飛び飛びですのであしからず...。)


通常、親コンポーネントから子コンポーネントには props を使って情報を渡します。しかし、props を多数の中間コンポーネントを経由して渡さないといけない場合や、アプリ内の多くのコンポーネントが同じ情報を必要とする場合、props の受け渡しは冗長で不便なものとなり得ます。コンテクスト (Context) を使用することで、親コンポーネントから props を明示的に渡さずとも、それ以下のツリー内の任意のコンポーネントが情報を受け取れるようにできます。

いわゆるバケツリレーで渡していくのは冗長ですよね。
渡したいコンポーネントに辿り着くまでに、経由するcomponent全てが情報知っている状態にもなります。

コンテクスト内では共通して使えるようにしておく、と言うことですね。

もし props を受け渡すことなしに、データを必要としているツリー内のコンポーネントに直接データを「テレポート」させる方法があれば、素晴らしいと思いませんか? React のコンテクスト機能を使えば、それが可能です!

えっ!?propsを使わずにコンポーネントにデータを!?
できらぁ!

例を見る

level を props としてそれぞれの に個別に渡しています。

// 公式から引用
<Section>
  <Heading level={3}>About</Heading>
  <Heading level={3}>Photos</Heading>
  <Heading level={3}>Videos</Heading>
</Section>

下記のようにして、Headingを操作できればスッキリしそうです。

<Section level={3}>
  <Heading>About</Heading>
  <Heading>Photos</Heading>
  <Heading>Videos</Heading>
</Section>

コンポーネントは、最も近い <Section> のレベルをどうやって知ることができるのでしょうか? これには、ツリー上部のどこかにあるデータを子コンポーネントが「要求する」方法が必要です。

Headingはなんとかして、Sectionの値を知りたいです。(Headinigに値をテレポートさせたい)
そんな時にコンテクスト が登場します。

ステップ 1:コンテクストを作成する

まずはコンテクストを作成する必要があります。複数のコンポーネントから使うことができるように、ファイルを作ってコンテクストをエクスポートする必要があります。

なるほど、では指定してみます。

// 公式から引用
// LevelContext.js
import { createContext } from 'react';
export const LevelContext = createContext(1);

createContextに渡した 1 というのがテレポートさせる値です。
(この時点ではまだuseContextは登場しません。)

createContext の唯一の引数はデフォルト値です。ここでは、1 という値は最大の見出しに対応するレベルを示していますが、任意の型の値(オブジェクトでも)を渡すことができます。デフォルト値の意味は次のステップで分かります。

createContextの引数(default値)に1を渡しましたが、何でも渡せそうです。

ステップ 2:コンテクストを使用する

この例はまだちゃんと動作していません。すべての見出しが同じサイズになってしまっているのは、コンテクストの使用はしているが、まだコンテクストの提供を行っていないからです。React はどこから値を取得すればいいのか分かりません!

現在、createContextに初期値として入れた1を呼び出せてはいますが、、

// 公式から引用
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  const level = useContext(LevelContext); // 絶対1になる
  switch (level) {
    case 1:
      return <h1>{children}{level}</h1>;
    case 2:
      return <h2>{children}{level}</h2>;
    case 3:
      return <h3>{children}{level}</h3>;
    case 4:
      return <h4>{children}{level}</h4>;
    case 5:
      return <h5>{children}{level}</h5>;
    case 6:
      return <h6>{children}{level}</h6>;
    default:
      throw Error('Unknown level: ' + level);
  }
}

createContextに値を渡して、それをuseContextの中に入れて使うじゃ足りないんですね。
コンテクストの提供というものをすればいいのか、どうすればSectionに指定した値を呼べるのだろう...

ステップ 3:コンテクストを提供する

ステップ2までの実装では、
LevelContext -> Heading.jsx
と値が渡されていました。これを
LevelContext -> Section.jsx -> Heading.jsx
とすれば良さそうです。

+ import { LevelContext } from './LevelContext.js';

export default function Section({ level, children }) {
  return (
    <section className="section">
+    <LevelContext.Provider value={level}>
        {children}
+    </LevelContext.Provider>
    </section>
  );
}

LevelContext.Provider というものが出てきました。(プロバイダって翻訳すると何?)
これを行うことで、LevelContext.Provider配下にいるcomponentに対して コンテクストの提供 が可能となります。

いったんまとめます。

porpsで値をバケツリレーせず、共通した値を孫にも渡したい
  • useContextを使う
    • 準備する const hoge = createContext(初期値)
    • 提供する <hoge.Provider value={値}> で囲む (親側で渡したい値を指定)
    • 呼び出す const huga = useContext(hoge) 値を呼び出す

...準備がややこしいんですよね!!!!

ただ、仕組みはわかりました!

もちろんオブジェクトも渡せます

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  const theme = {
    mode : "dark",
    font : 20
  }
  return (
    <ThemeContext.Provider value={theme}>
      <Form />
    </ThemeContext.Provider>
  )
}

まとめ

useContextを使うべく、公式を追ってみました。
準備が必要という時点でちょっと複雑に感じますが、わかってしまえば大丈夫ですね!

もう一歩深掘るには createContext を理解する必要がありそうです!
(何を返すのか、中身がどうなっているのか)

次回は createContext を追ってみることにします!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0