Reactで子コンポーネントから親コンポーネントを操作するサンプル

## はじめに
タイトル通りReactで子コンポーネントから親コンポーネントを操作する方法についてまとめました．
既存記事はありますが，教材用として車輪を再発明します．

## やりたいこと
Reactでは親コンポーネントから子コンポーネントに対して値を渡すことは容易です．また，適切に`UseState`を使えば，親コンポーネントで値を更新した時コンポーネントの値も自動で更新されます．
しかし，同様の手順では子コンポーネント内の値更新を親コンポーネントに反映させることはできません．
この記事では，この動作の実現を目標とします．
![コンポーネント間の値のやり取り](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644068/e06d3b3f-5b13-db45-7b3c-33109a3dfb82.png)


## コンポーネントの用意
ここでは`App`コンポーネントから`SubComponent`コンポーネントを呼び出す状況を想定します．つまり`App`で管理する値を`SubComponent`コンポーネントから変更することが目標です．
環境は`npm create vite`で作成しました．

### Appコンポーネント
とりあえず`App`コンポーネントに書かれているコードはほぼ消して，`App`コンポーネントで管理する値を用意します．
```tsx:App.tsx
import { useState } from "react";
import "./App.css";

function App() {
  const [appValue, setAppValue] = useState(0);

  return (
    <>
      <p>appValue = {appValue}</p>
    </>
  );
}

export default App;
```

### SubComponentコンポーネント
`SubComponent`コンポーネントでもそこで管理する値を用意します．また，その値を変えるためのボタンも配置します．

```tsx:SubComponent.tsx
import { useState } from "react";
import "./App.css";

function SubComponent() {
  const [subValue, setSubValue] = useState(0);

  return (
    <>
      <p>subValue = {subValue}</p>
      <button onClick={() => setSubValue(0)}>subValue = 0</button>
      <button onClick={() => setSubValue(1)}>subValue = 1</button>
      <button onClick={() => setSubValue(2)}>subValue = 2</button>
    </>
  );
}

export default SubComponent;
```

## SubComponentからAppコンポーネントの値を変更する
### AppコンポーネントでSubComponentを呼び出す
1行加えるだけでOKです．

```diff_tsx:App.tsx
import { useState } from "react";
import "./App.css";

function App() {
  const [appValue, setAppValue] = useState(0);

  return (
    <>
      <p>appValue = {appValue}</p>
+     <SubComponent />
    </>
  );
}

export default App;
```
これで画面は以下のようになります．
![現在の画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644068/98c510a2-85b8-5314-0a6b-58127e5c03ce.png)

### コールバック関数の定義
`SubComponent`から`App`コンポーネントの値を変更するために，`App`コンポーネントの`setState`（今回は`setAppValue`）を受け取れるようにします．このとき`setAppValue`を引数として受け取ります．ある関数Aに引数として渡される関数Bのことをコールバック関数といいます．
コールバック関数の型は`interface`で定義し，`setAppValue`は`setValue`という名前で受け取ることにしました．
```diff_tsx:SubComponent.tsx
import { useState } from "react";
import "./App.css";

+interface SubComponentProps {
+  setValue: (value: number) => void;
+}

+ function SubComponent({ setValue }: SubComponentProps) {
  const [subValue, setSubValue] = useState(0);

  return (
    <>
      <p>subValue = {subValue}</p>
      <button onClick={() => setSubValue(0)}>subValue = 0</button>
      <button onClick={() => setSubValue(1)}>subValue = 1</button>
      <button onClick={() => setSubValue(2)}>subValue = 2</button>
    </>
  );
}

export default SubComponent;
```

### コールバック関数の使用
受け取った`setValue`を使うために，buttonで呼び出していた関数を変更します．
```diff_tsx:SubComponent.tsx
import { useState } from "react";
import "./App.css";

interface SubComponentProps {
  setValue: (value: number) => void;
}

function SubComponent({ setValue }: SubComponentProps) {
  const [subValue, setSubValue] = useState(0);

  return (
    <>
      <p>subValue = {subValue}</p>
+      <button onClick={() => setSubValue(0)}>setValue = 0</button>
+      <button onClick={() => setSubValue(1)}>setValue = 1</button>
+      <button onClick={() => setSubValue(2)}>setValue = 2</button>
    </>
  );
}

export default SubComponent;
```

これで`SubComponent`コンポーネントからの準備は終わりました．
次に，`App`コンポーネントから`SubComponent`コンポーネントに`setValue`を渡せるようにします．

```diff_tsx:App.tsx
import { useState } from "react";
import "./App.css";
import SubComponent from "./SubComponent";

function App() {
  const [appValue, setAppValue] = useState(0);

  return (
    <>
      <p>appValue = {appValue}</p>
+     <SubComponent setValue={setAppValue} />
    </>
  );
}

export default App;
```
これで下図のように`SubComponent`のボタン操作で`App`コンポーネントの値を変更できるようになりました．
![値の変更ができるようになった時の図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644068/60303d57-f8c4-34f5-5180-4b1d51b11aeb.png)

### 値の受取
準備段階では`SubComponent`上での値が変更できることを確認するために，`useState`の`subValue`と`setSubValue`を定義していました．しかし，上でボタンの`onClick`にコールバック関数として渡していた`setSubValue`は`setValue`に置き換えました．つまり`setSubValue`は不要です．`SubComponent`コンポーネントで`App`コンポーネントと独立した値を管理する必要も無いので，`subValue`と合わせて削除してしまいます．

```diff_tsx:SubComponent.tsx
-import { useState } from "react";
import "./App.css";

interface SubComponentProps {
  setValue: (value: number) => void;
}

function SubComponent({ setValue }: SubComponentProps) {
- const [subValue, setSubValue] = useState(0);

  return (
    <>
+     <p>subValue = {}</p>
      <button onClick={() => setValue(0)}>subValue = 0</button>
      <button onClick={() => setValue(1)}>subValue = 1</button>
      <button onClick={() => setValue(2)}>subValue = 2</button>
    </>
  );
}

export default SubComponent;
```

`SubComponent`コンポーネント上でも`App`コンポーネントで管理する値を表示したくなる場合もあるかもしれません．そのときは`interface`を変更して値を受け取れるようにします．

```diff_tsx:SubComponent.tsx
import "./App.css";

interface SubComponentProps {
  setValue: (value: number) => void;
+ value: number;
}

+function SubComponent({ setValue, value }: SubComponentProps) {

  return (
    <>
+     <p>subValue = {value}</p>
      <button onClick={() => setValue(0)}>subValue = 0</button>
      <button onClick={() => setValue(1)}>subValue = 1</button>
      <button onClick={() => setValue(2)}>subValue = 2</button>
    </>
  );
}

export default SubComponent;
```

`SubComponent`コンポーネントの変更に合わせて，`App`コンポーネントから値を渡せるようにします．
```diff_tsx:App.tsx
import { useState } from "react";
import "./App.css";
import SubComponent from "./SubComponent";

function App() {
  const [appValue, setAppValue] = useState(0);

  return (
    <>
      <p>appValue = {appValue}</p>
+     <SubComponent setValue={setAppValue} value={appValue} />
    </>
  );
}

export default App;
```

このようにどちらの値も連動して変化するようになりました．
![値を渡せるようになった時の図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3644068/d36257df-ecad-2e9d-a5d7-1dd13a3f6519.png)