React入門 未経験から1週間でReactをマスターする #05. フォームと親子間のデータのやり取り

# 目次

[1. Reactの新規プロジェクトの立ち上げ](https://qiita.com/yassun-youtube/items/2ae26050efd2133c2286)
[2. コンポーネントのプロパティ(props)とステート(state)](https://qiita.com/yassun-youtube/items/ca91e2f9905fb8ca62d0)
[3. Class Components と Function Components] (https://qiita.com/yassun-youtube/items/2ed8601e4fa477726705)
[4. 条件分岐 (if) と繰り返し (loop)](https://qiita.com/yassun-youtube/items/9474b1681958e2ab0f25)
[5. フォームと親子間のデータのやり取り](https://qiita.com/yassun-youtube/items/545e7d9c98fe919dc394)<font color="Red">←今ここ</font>
[6. コンポーネントのライフサイクル](https://qiita.com/yassun-youtube/items/372464404ae6855e6d78)
7. スタイル (準備中)
8. Higher-Order Component (準備中)
9. Portalを利用したモーダル (準備中)
10. refによるエレメントの取得 (準備中)
11. Contextを利用したテーマの変更 (準備中)

## 今回の学習内容

今回は、

- Reactでのフォームの利用方法
- 親子間でのデータのやりとり

をやっていきます。

# YouTubeでの解説動画

YouTubeでも解説しています。
動画で確認したい方はこちらもどうぞ。
[【YouTube動画】 未経験から1週間でをマスターするReact入門 #05. フォームと親子間のデータのやり取り](https://youtu.be/kEBP9WhifX0)
[<img src="https://img.youtube.com/vi/kEBP9WhifX0/0.jpg" alt="未経験から1週間でをマスターするReact入門 #05. フォームと親子間のデータのやり取り">](https://youtu.be/kEBP9WhifX0)


# この記事のソースコード
ソースコードはGitHubで公開しています。

https://github.com/yassun-youtube/ReactTutorial

今回のコミット一覧↓

![スクリーンショット 2020-12-01 13.16.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751408/94e2b6fc-6e9a-29f5-a7a0-bc75c8405dea.png)


# フォームの利用

まずはReactでフォームを利用してみます。
`Form.js` にフォームを入れます。

このWebアプリでは、言語のリストと追加を行うアプリにしたいので、新しい言語を追加するフォームを作ります。

```jsx:src/Form.js
import { useState } from 'react'; // 追加

export const Form = () => {
  const [text, setText] = useState(''); // 追加

  const submitForm = (e) => { // 追加
    e.preventDefault();
    console.log(`submitForm(${text})`);
  }

  return (
    <div>
      { /* 変更 */ }
      <h4>新しい言語の追加</h4>
      <form onSubmit={submitForm}>
        <div>
          <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
        </div>
        <div>
          <button>追加</button>
        </div>
      </form>
    </div>
  )
}
```

## コードの説明

```jsx
  const [text, setText] = useState(''); // 追加
```

まずここで、 `text` という state を追加しています。

```jsx
  const submitForm = (e) => { // 追加
    e.preventDefault();
    console.log(`submitForm(${text})`);
  }
```

次に、 `form` をサブミットされたときの関数を定義しました。

ここでは、 `preventDefault()` をしてコンソールにログを表示するようにしています。

```jsx
<h4>新しい言語の追加</h4>
<form onSubmit={submitForm}>
  <div>
    <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
  </div>
  <div>
    <button>追加</button>
  </div>
</form>
```

`form` タグに `onSubmit` を定義しています。これで `form` がサブミットされると 先程定義した `submitForm` 関数が呼び出されます。

```jsx
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
```

次にこの `input` タグですが、これは `text` state を値としてもち、`onChange` を利用することで変更検知を行って `text` stateを `setText`  を行うことで変更しています。

こうすることで、 `text` を常に `input` の内容と一致させることができます。

実際に動かしてみると下図のようになります。

![7c9b5ec202b5788520e1189e570a9f41.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751408/85012712-2d70-2451-d5f0-9f67e7b57a8c.gif)

## ここまでのコミット

https://github.com/yassun-youtube/ReactTutorial/commit/1fa65388d4662879c48b15ffd4f0d2ab2c0d915a

# 親子間のデータのやりとり

次に、フォームに入力されるとリスト側の言語が増えるようにしてみましょう。

今は `List.js` が言語データを管理しているのｄせうが、言語データを `App.js` が管理するようにします。
そこでまず、前回 `List.js` で定義していた `LANGUAGE` 定数を別ファイル( `src/const/languages.js` )に移します。

```jsx:src/const/languages.js
export const LANGUAGES = [
  'JavaScript',
  'C++',
  'Ruby',
  'Java',
  'PHP',
  'Go'
];
```

それを利用して、 `App.js` を修正します。

```jsx:src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages"; // 追加

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES); // 追加

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List /> : <Form />
      }
    </div>
  );
}

export default App;
```

言語データを管理するために `langs` という state を定義しました。

## Form.js => App.js のデータのやり取り

次に、子コンポーネントである `Form.js` から `App.js` へのデータの受け渡しをします。

子から親へのデータの受け渡しは、 `props` を利用します。
具体的には、`props` に関数への参照を渡すことで、 子コンポーネントからその関数を呼び出してもらいます。

```jsx:src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES);

  const addLang = (lang) => { // 追加
    console.log(lang);
    setLangs([...langs, lang])
  }

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List /> : <Form onAddLang={addLang}/> // 変更
      }
    </div>
  );
}

export default App;
```

`App.js` 側に `addLang` 関数を作成し、 `Form.js` のプロパティとして渡してみました。
これを `Form.js` から呼び出してみます。

```jsx:src/Form.js
import { useState } from 'react';

export const Form = ({ onAddLang }) => { // 変更
  const [text, setText] = useState('');

  const submitForm = (e) => {
    e.preventDefault();
    onAddLang(text); // 変更
  }

  return (
    <div>
      <h4>新しい言語の追加</h4>
      <form onSubmit={submitForm}>
        <div>
          <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
        </div>
        <div>
          <button>追加</button>
        </div>
      </form>
    </div>
  )
}
```
## コード解説

`Form.js` では、 `onAddLang` という `props` を追加しています。これは関数への参照になります。
`submitForm` 関数内で `onAddLang` 関数を呼び出すことにより、 `App.js` でイベントを起こします。

`App.js` では、

```jsx
 <Form onAddLang={addLang}/>
```

と定義していますので、 `Form.js` から `onAddLang` が呼ばれると、 `App.js` 内ので `addLang` 関数が呼ばれることになります。

```jsx
  const addLang = (lang) => { // 追加
    console.log(lang);
    setLangs([...langs, lang])
  }
```

`addLang` 関数では、 `App.js` の `langs` に対してデータを追加するようになっているので、データが追加されます。

## 実際に動かしてみる

実際に動かしてみて、 `Form.js` から `App.js` の関数が呼ばれていることを確認します。

今は `App.js` 内でコンソールに渡された値を表示することで確認できます。

![a304ddb31fd27e65e4b1145478a12b19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751408/b9a02d43-5124-6982-e003-8df58614301f.gif)

# 追加された言語をリストへ反映

次に、追加された言語を `List.js` に反映してみます。

```jsx:src/App.js
import { useState } from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

function App() {
  const [tab, setTab] = useState('list');
  const [langs, setLangs] = useState(LANGUAGES);

  const addLang = (lang) => {
    setLangs([...langs, lang]);
    setTab('list'); // 追加
  }

  return (
    <div>
      <header>
        <ul>
          <li onClick={() => setTab('list')}>リスト</li>
          <li onClick={() => setTab('form')}>フォーム</li>
        </ul>
      </header>
      <hr />
      {
        tab === 'list' ? <List langs={langs} /> : <Form onAddLang={addLang}/> // 変更
      }
    </div>
  );
}

export default App;
```

フォームから言語が追加されたら `List.js` を表示するために、`addLang` 関数で追加されたらタブも変更するようにしました。
また、 `List.js` に `langs` stateを渡すようにしています。

次に、 `List.js` でこの渡された `langs` を利用するようにしましょう。

```jsx
export const List = ({ langs }) => { // 変更
  return (
    <div>
      {
        langs.map((lang, index) => { // 変更
          return <div key={index}>{ lang }</div>
        })
      }
    </div>
  )
}
```

`List.js` からは、 `src/const/languages.js` に移動した `LANGUAGES` を削除しました。
また、 `props` から `langs` を受け取り、それを利用するようにしています。

これにより、下図のようにフォームで追加された内容が `List.js` に反映されるようになりました。

![bbd3365b2696cd719367109b84227851.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751408/e279bc62-346e-4e1d-900e-b39e272601db.gif)

# 今日やったこと

## JSXでのフォームの利用

```jsx
<form onSubmit={submitFunc}>
  <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
  <button>ボタン</button>
</form>
```

JSXでフォームを利用するためには、 `onSubmit` イベントで関数を呼び出します。
`input` 属性は、 `value` と `onChange` を利用して状態を管理します。

## 親子間のデータのやり取り
### 子コンポーネント=>親コンポーネント

```jsx
function Parent() {
  const testFunc = () => {}
  return <Child parentFunc={testFunc} />
}

function Child({ parentFunc }) {
  return <div onClick={() => parentFunc(text)}>テスト</div>
}
```

子コンポーネントから親コンポーネントにデータを渡すには、 `props` に渡された関数を子コンポーネントから呼び出すようにします。

### 親コンポーネント=>子コンポーネント

```jsx
function Parent() {
  const test = 'テストデータ';
  return <Child parentData={test} />
}

function Child({ parentData }) {
  return <div>{ parentData }</div>
}
```

親コンポーネントから子コンポーネントにデータを渡すには、 `props` でデータを渡します。

# Class Components

<details>
  <summary>Class Components でのコードはこちら</summary>
  <div>

## App.js

- stateの追加
- List.jsへlangsを渡す
- Form.jsから新しいlangを受け取る

を追加しています。

```src/App.js
import React from 'react';
import { List } from "./List";
import { Form } from "./Form";
import { LANGUAGES } from "./const/languages";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tab: 'list',
      langs: LANGUAGES,
    };
  }
  addLang(lang) {
    this.setState({
      langs: [...this.state.langs, lang],
      tab: 'list',
    });
  }
  render() {
    const { tab, langs } = this.state;
    return (
      <div>
        <header>
          <ul>
            <li onClick={() => this.setState({ tab: 'list' })}>リスト</li>
            <li onClick={() => this.setState({ tab: 'form' })}>フォーム</li>
          </ul>
        </header>
        <hr />
        {
          tab === 'list' ? <List langs={langs} /> : <Form onAddLang={(lang) => this.addLang(lang)}/>
        }
      </div>
    )
  }
}

export default App;
```

## Form.js

`Form.js` は全体的に変更します。
`this` のことをケアしてアロー関数で書くことに注意してください。

```jsx:src/Form.js
import React from 'react';

export class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: '' }
  }
  submitForm(e) {
    e.preventDefault();
    this.props.onAddLang(this.state.text);
  }

  render() {
    const { text } = this.state;
    return (
      <div>
        <h4>新しい言語の追加</h4>
        <form onSubmit={(e) => this.submitForm(e)}>
          <div>
            <input type="text" value={text} onChange={(e) => this.setState({ text: e.target.value })} />
          </div>
          <div>
            <button>追加</button>
          </div>
        </form>
      </div>
    )
  }
}
```

## List.js


```jsx:src/List.js
import React from 'react';
// LANGUAGES定数を削除

export class List extends React.Component {
  render() {
    const { langs } = this.props; // propsからlangsを利用
    return (
      <div>
        {
          langs.map((lang, index) => {
            return <div key={index}>{ lang }</div>
          })
        }
      </div>
    )
  }
}
```

## languages.js

ファイルを追加

```jsx:src/const/languages.js
export const LANGUAGES = [
  'JavaScript',
  'C++',
  'Ruby',
  'Java',
  'PHP',
  'Go'
];
```

  </div>
</details>

こちらのコミットの方が変更点がわかりやすく良いかもしれません。

https://github.com/yassun-youtube/ReactTutorial/commit/61654d4069d3888f14f90aced3aee73e1a292b53?branch=61654d4069d3888f14f90aced3aee73e1a292b53&diff=split

# おわりに

これで、 `Form.js` ⇒ `App.js` の子コンポーネントから親コンポーネントへのデータ渡し、および `App.js` ⇒ `List.js` の親コンポーネントから子コンポーネントへのデータ渡しを実装できました。

# 次の記事

React入門 未経験から1週間でReactをマスターする #06. コンポーネントのライフサイクル
https://qiita.com/yassun-youtube/items/545e7d9c98fe919dc394