React.jsで複数のフォームを扱う方法


React.jsでwebアプリを作りましたが、Reactのフォームは情報が少ないと感じたのでまとめます。
自分の復習もかねて出来るだけ丁寧に書いてみました。

間違っている点がありましたらご指摘いただけると嬉しいです。


# この記事でわかること
1つのComponentに複数のフォームを用意して、入力した内容を画面上に追加していく方法。

# 前提条件

・JavaScriptの基礎知識
・React.jsの基礎知識（チュートリアルできた！くらいでOK）

下記のように、2つのフォームに入力した内容をonClickで画面下に追加していくwebアプリを作ってみます。

# 完成形
![180708.gif](https://qiita-image-store.s3.amazonaws.com/0/267607/7c2bb422-9095-ec60-e3c7-c581ebc66b28.gif)

# では作ってみよう！

## Reactフォルダの準備

まずcreate-react-appで新しいフォルダを作ります。


```react:App.js

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>

      </div>
    );
  }
}

export default App;
```

imgなどを削除しシンプルにしました。
ここからフォームを追加していきましょう

## stateとフォームの追加

```react:App.js
import React, { Component } from 'react';

class App extends Component {
constructor(props){
  super(props);
  this.state = {
    title:"",
    author:""
}
}
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>
        <p>タイトル</p>
        <input type="text" name="title"/>
        <p>作者</p>
        <input type="text" name="author"/>
      </div>
    );
  }
}
export default App;
```

titleとauthorという名前のstateを作り、inputも2つ追加しました。
ここで必ずinputのnameとstateの名前が同じになるようにしてください。

## フォームにhandleChangeイベントの追加
```react:App.js
import React, { Component } from 'react';
//省略
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>
        <p>タイトル</p>
        <input type="text" name="title" onChange={this.handleChange}/>
        <p>作者</p>
        <input type="text" name="author" onChange={this.handleChange}/>
      </div>
    );
  }

  handleChange = (event) => {
    this.setState({[event.target.name]: event.target.value})
  }
}

export default App;
```
各フォームにhandleChangeイベントを追加し、handleChangeに関する記載を追加しました。

### setState部分の解説

handleChange内のsetStateメソッドで更新するstateと新しいstateの値を指定しています。
更新するstate名は[event.target.name]として指定していますが、これが先ほどstate名とinputのnameを揃えた理由です。

通常でしたら
this.setState({title: event.target.value})
のようにstate名を直接書きますが、これでは1つのフォームしか扱えません。

[event.target.name]とすることで、イベントが発生した(つまり文字入力がされた)フォームのnameを参照し、それがそのまま更新するstateとして指定されます。
例えばname="title"のフォームでhandleChangeイベントが発生した場合、[event.target.name]は"title"となり、this.setState({title: event.target.value})と書いた場合と同じになるわけです。
これで1つのhandleChangeイベントで複数のstateの値を更新できます。

次はstateの値を送信してみます

## handleSubmitイベントの追加

```react:App.js
import React, { Component } from 'react';

class App extends Component {
constructor(props){
  super(props);
  this.state = {
    lists:[],
    title:"",
    author:""
}

}
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>
          <p>タイトル</p>
          <input type="text" name="title" onChange={this.handleChange} value={this.state.title}/>
          <p>作者</p>
          <input type="text" name="author" onChange={this.handleChange} value={this.state.author}/>
          <button onClick={this.handleSubmit}>
            send
          </button>
      </div>
    );
  }

  handleChange = (event) => {
    this.setState({[event.target.name]: event.target.value})
  }

  handleSubmit = (event) => {
    event.preventDefault();
    this.setState({
      lists: [
          ...this.state.lists,
          { 
            title: this.state.title,
            author: this.state.author
          }],
      title:"",
      author:""
  });
}}

export default App;
```

フォームの下にbuttonを追加し、クリックされた際にhandleSubmitイベントを呼び出すようにしました。
また、stateに新しくlists:[]を追加しました。handleSubmitが呼び出された際に、フォームに入力された内容がここに入ります。
hundleSubmitの詳細はrenderの下に記載しています。


### 新しいstate "lists:[]"

handleSubmitのイベントが呼ばれた際にtitle, authorのstateに入っている情報が配列listsに入ります。
listsはフォームに入力された内容をオブジェクト{title:..., author:...}として持っており、sendボタンが押されるたびに新しいオブジェクトが配列の最後に追加されます。


### setState部分の解説
#### ...lists, とは

...this.state.listsの部分では今のsetStateのlist配列を展開し、その後ろの
 { title: this.state.title, author: this.state.author }
の部分でフォームに入力されたtitle, authorの値を配列listsの最後に追加します。
...this.state.listsがないと2回目以降のonClickの際に、今までにlists:[]に追加した内容が消えてしまうので注意してください。

#### stateとフォームの内容のリセット
最後のtitle:"", author:""はtitle, authorのstateと、入力したフォームをリセットするためです。
stateを空にしただけではフォームに入力した内容が残るので、inputにvalue={this.state.title}とvalue={this.state.author}を追加しました。これでtitle, authorのstateが空になると同時にフォームも空になります。


ここまでできたら一度フォームに入力してsendボタンをクリックし、listsの中身を確認してみてください。入力した内容が配列listsの中にオブジェクトとして入っているはずです。
ではこれを画面上にも表示させましょう。

## 配列listsに追加された内容の表示

```react:App.js
import React, { Component } from 'react';

//省略
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>
          <p>タイトル</p>
          <input type="text" name="title" onChange={this.handleChange} value={this.state.title}/>
          <p>作者</p>
          <input type="text" name="author" onChange={this.handleChange} value={this.state.author}/>
          <button onClick={this.handleSubmit}>
            send
          </button>

          {this.state.lists.map((l)=>(
          <div key={l.title}>
            <p>title:{l.title}</p>
            <p>author:{l.author}</p>  
          </div>        
          ))}
      </div>
    );
  }

//省略

export default App;
```

ボタンから下の部分を追加しました。
先ほど書いたように、onSubmitで送信された内容は配列listsの中に入ります。
そこで配列listsの中身をmapで要素(オブジェクト)ごとに呼び出して、jsxを返していきます。

これで配列lists内に入れられた各要素を表示することができました！

以上、React.jsで複数のフォームの内容を扱う方法でした。


# 完成形のコード
```react:App.js
import React, { Component } from 'react';

class App extends Component {
constructor(props){
  super(props);
  this.state = {
    lists:[],
    title:"",
    author:""
}

}
  render() {
    return (
      <div className="App">
        <p>タイトルと作者</p>
          <p>タイトル</p>
          <input type="text" name="title" onChange={this.handleChange} value={this.state.title}/>
          <p>作者</p>
          <input type="text" name="author" onChange={this.handleChange} value={this.state.author}/>
          <button onClick={this.handleSubmit}>
            send
          </button>

          {this.state.lists.map((l)=>(
          <div key={l.title}>
            <p>title:{l.title}</p>
            <p>author:{l.author}</p>  
          </div>        
          ))}
      </div>
    );
  }

  handleChange = (event) => {
    this.setState({[event.target.name]: event.target.value})
  }

  handleSubmit = (event) => {
    event.preventDefault();
    this.setState({
      lists: [
          ...this.state.lists,
          { 
            title: this.state.title,
            author: this.state.author
          }],
      title:"",
      author:""
  });
}}

export default App;
```

## 参考サイト
[Handling Multiple Form Inputs in React (Medium)](https://medium.com/@tmkelly28/handling-multiple-form-inputs-in-react-c5eb83755d15)