8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React】useContextの使い方 💡

Last updated at Posted at 2023-01-22

useContextとは

React Hooksの一つで、グローバルにデータを管理することができる仕組みです。
通常親コンポーネントから子コンポーネントにデータを渡す際はpropsを介して行うかと思います。
しかし親から子、そのまた子といったように複数のコンポーネントを介してデータを渡す場合にpropsでのバケツリレーのようなやり方では設定が複雑になってきます。
useContextを使用することでpropsを利用することなく異なる階層のコンポーネントとデータの共有を行うことができます。

グローバルに「データ」を管理することができるもの。
「props」によるバケツリレーを防ぐために利用する。

シンプルなコード例

下記のようなディレクトリ構造でuseContextを使用してみます。

├ Example.js
├ components/
   ├ Child.js
   ├ GrandChild.js

Example.jsx
import Child from "./components/Child";
import { createContext } from "react";
export const MyContext = createContext("useContextのテスト");
const Example = () => {
  return <Child />;
};
export default Example;
Child.jsx
import GrandChild from "./GrandChild";

const Child = () => (
  <div style={{ border: "1px solid black", padding: 10 }}>
    <h3>子コンポーネント</h3>
    <GrandChild />
  </div>
);

export default Child;
GrandChild.jsx
import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
  const value = useContext(MyContext);
  return (
    <div style={{ border: "1px solid black" }}>
      <h3>孫コンポーネント</h3>
      {value}
    </div>
  );
};
export default GrandChild;

上記関数コンポーネントを実行していただくと下記のようにpropsを使用することなく、孫コンポーネントにデータを渡せていることがわかるかと思います。
スクリーンショット 2023-01-16 19.12.06.png

コード解説

useContextを使用するには、reactからcreateContext()を読み込む必要があります。

Example.js
import { createContext } from "react";

そのcreateContext()を使用し、コンテキストを定義し、exportします。
createContext()の引数にはpropsを通さずに渡したい値を記述します。

Example.js
export const MyContext = createContext("useContextのテスト");

そしてGrandChildコンポーネントで使用したいので、importしています。
ここでuseContext()reactから読み込みます。

GrandChild.js
import { useContext } from "react";
import { MyContext } from "../Example";

最後にuseContext()の引数にimportしたMyContextを渡すことで、設定しておいた値を取得することができます。

GrandChild.js
const value = useContext(MyContext);
// value = useContextのテスト

stateとuseContextを組み合わせて使う方法

次にネスト構造になっていない、コンポーネント間でクリックを検知して、stateを変更する方法について解説します。
下記のような構造で作成します。

├ Example.js
├ components/
   ├ Child.js
   ├ GrandChild.js
   ├ Other.js

Otherコンポーネントの兄弟に、Childコンポーネントがあり、そのChildコンポーネントの中にはGrandChildコンポーネントがあるような構造です。
Otherコンポーネントのクリックを検知し、GrandChirdにあるstateを変更させます。

Example.jsx
import { createContext, useState } from "react";
import Child from "./components/Child";
import OtherChild from "./components/OtherChild";
export const MyContext = createContext();

const Example = () => {
  const [value, setValue] = useState(0);
  return (
    <MyContext.Provider value={[value, setValue]}>
      <Child />
      <OtherChild />
    </MyContext.Provider>
  );
};

export default Example;
Other.jsx
import { useContext } from "react";
import { MyContext } from "../Example";

const OtherChild = () => {
  const [, setValue] = useContext(MyContext);
  const clickHandler = (e) => {
    setValue((prev) => prev + 1);
  };

  return (
    <div>
      <h3>他の子コンポーネント</h3>
      <button onClick={clickHandler}>+</button>
    </div>
  );
};

export default OtherChild;
Child.jsx
import GrandChild from "./GrandChild";
const Child = () => (
  <div style={{ border: "1px solid black", padding: 10 }}>
    <h3>子コンポーネント</h3>
    <GrandChild />
  </div>
);
export default Child;
GrandChild.jsx
import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
  const [value] = useContext(MyContext);
  return (
    <div style={{ border: "1px solid black" }}>
      <h3>孫コンポーネント</h3>
      {value}
    </div>
  );
};
export default GrandChild;

上記コンポーネントを実行すると、下記のように他のコンポーネントで作成したボタンをクリックすると、孫コンポーネントにあるstate(カウント)が変化するようになります。

スクリーンショット 2023-01-18 20.52.23.png

コード解説

まず、stateを使用するコンポーネントを全て読み込んでいる場所でuseStatecontextを定義します。

Example.js
import { createContext, useState } from "react";
import Child from "./components/Child";
import OtherChild from "./components/OtherChild";
export const MyContext = createContext(); // context定義

const Example = () => {
  const [value, setValue] = useState(0); // state定義
  ...
};

定義したcontext(MyContext)のProviderというコンポーネントでstateを使用するコンポーネントを包装します。
そのProviderコンポーネントのvalueを設定することで、valueにセットされた値がuseContextを通してネストされたコンポーネントで取得することができるようになります。
今回はvalueに設定する値は定義しておいた、stateとその変更関数ごと、配列のまま渡します。

Example.jsx
<MyContext.Provider value={[value, setValue]}> 
  <Child />
  <OtherChild />
</MyContext.Provider>

useContextの挙動とすると順々に親のコンポーネントを参照していき、Providerコンポーネントのvalueに設定された値を取得します。
Providerコンポーネントのvalueを設定しなかった場合、createContext()時に渡された引数を取得します。
例)
export const MyContext = createContext('これが初期の値になるよ');

ボタンが作成されているOther.jsではcontextをimportし、useContext()の引数に渡します。

Other.jsx
import { useContext } from "react"; 
import { MyContext } from "../Example"; // 定義したMyContextのimport

それをuseContext()の引数に渡し、返り値を格納することでvalueに設定された値を取得できます。
valueには[value, setValue]の配列を渡してありますが、このコンポーネントではstateは使いませんので、分割代入で1番目の要素であるsetValue、つまりstateの変更関数だけを取得します。

Other.js
 const [, setValue] = useContext(MyContext); // 使用する変更関数のみ取得

そしてボタンのonClickイベントでclickHandlerを実行させます。
clickHandler()setValuestateの値に+1をする関数です。

Other.js
 const clickHandler = (e) => {
    setValue((prev) => prev + 1);
  };

stateを表示しているGrandChild.jsも同じ要領で、useContext()の引数にcontext、つまりvalueに設定したstateの値のみ取得して表示させているという流れです。

GrandChild.jsx
import { useContext } from "react";
import { MyContext } from "../Example"; // contextのimport
const GrandChild = () => {
  const [value] = useContext(MyContext); // 分割代入でstateのみ取得
  return (
    <div style={{ border: "1px solid black" }}>
      <h3>孫コンポーネント</h3>
      {value}
    </div>
  );
};
export default GrandChild;
8
6
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
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?