Reactで孫のイベントで親のステートを変更する(コンポジションとコンテクスト)

目的

孫のイベントで親のステートを変更したい。

環境

  • Node.js v14.15.0
  • NPM v6.14.8
  • React v17.0.1
  • create-react-app v4.0.1

実装例

実装パターンを二通り試した。

コンポジションとは

https://reactjs.org/docs/composition-vs-inheritance.html
コンポジションと継承について、いくつかの例とともに書かれている。
https://reactjs.org/docs/context.html#before-you-use-context
contextを使う前にコンポジションを考慮しようと書かれている。

実装例

Parent.js

import { useState } from "react";
import { Child } from "./Child";
import { GrandChild } from "./GrandChild";

export const Parent = () => {
  const [count, setCount] = useState(0);
  const countUp = () => {
    setCount(count + 1);
  };
  const grandChild = (<GrandChild countUp={countUp}></GrandChild>)
  return (
    <div>
      <p>Parent component</p>
      <p>{count}</p>
        <Child grandChild={grandChild}></Child>
    </div>
  );
};

Child.js

export const Child = (props) => {
  return (
    <div>
      <p>Child component</p>
      {props.grandChild}
    </div>
  );
};

GrandChild.js

export const GrandChild = (props) => {
  return (
    <div>
      <p>GrandChild Component</p>
      <button onClick={props.countUp}>
        GrandChild Button
      </button>
    </div>
  );
};

コンテクストとは

https://reactjs.org/docs/context.html
コンテクストの概念について書かれている。
https://reactjs.org/docs/hooks-reference.html#usecontext
hooksで実装するパターンについて書かれている。

実装例

Parent.js

import { useState } from "react";
import { Child } from "./Child";
import { ClickEventContext } from "./hogeContext";

export const Parent = () => {
  const [count, setCount] = useState(0);
  const countUp = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>Parent component</p>
      <p>{count}</p>
      <ClickEventContext.Provider value={{ onClickEvent: countUp }}>
        <Child></Child>
      </ClickEventContext.Provider>
    </div>
  );
};

Child.js

import { GrandChild } from "./GrandChild";

export const Child = () => {
  return (
    <div>
      <p>Child component</p>
      <GrandChild></GrandChild>
    </div>
  );
};

GrandChild.js

import { useContext } from "react";
import { ClickEventContext } from "./hogeContext";

export const GrandChild = () => {
  const clickEventContext = useContext(ClickEventContext)
  return (
    <div>
      <p>GrandChild Component</p>
      <button onClick={clickEventContext.onClickEvent}>GrandChild Button</button>
    </div>
  );
};

GrandChildのクリックイベント用コンテキスト
ClickEventContext.js

import { createContext } from "react";
export const ClickEventContext = createContext({ onClickEvent: () => {} });

まとめ

今回の用途ならContextAPIをせずコンポジションのほうが良いと判断した。

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.