ReactでNetlifyのFormsを利用する方法

Reactで作ったプロジェクトをNetlifyでホスティングしている時に、NetlifyのFormsを使おうと思ったら素直には行かなかったので。

[「ここ」](https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/)に書いてあることです。

通常NetlifyでFormsを利用する場合は、`form`に`netlify`属性を追加することでNetlifyがHTMLをパースする時にコレを読み取ってFormsが利用できるようになりますが、残念ながらReactのプロジェクトでは単純にJSXに`netlify`属性を追加しただけではFormsを利用することはできません。

## シンプルにフォームを作成する

１番簡単にReactでフォームを実装する方法は次のような形になります。

```react
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Contact</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">

      ReactDOM.render(
        <form name="contact" method="post">
          <p>
            <label>Your Name: <input type="text" name="name"/></label>
          </p>
          <p>
            <label>Your Email: <input type="email" name="email"/></label>
          </p>
          <p>
            <label>Message: <textarea name="message"></textarea></label>
          </p>
          <p>
            <button type="submit">Send</button>
          </p>
        </form>,
        document.getElementById("root")
      );

    </script>
  </body>
</html>
```

コレを元に進めて行きます。

### 静的なHTMLのフォームをサイトに追加する

JSXに記述してもFormsを利用することはできないため、NetlifyにFormsの利用を知らせるために静的なHTMLのフォームをソースに追加します。(`public/index.html`など)

```react

<!-- A little help for the Netlify post-processing bots -->
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
  <input type="text" name="name" />
  <input type="email" name="email" />
  <textarea name="message"></textarea>
</form>
```

ここで出て来た`netlify-honeypot`は、ユーザーが利用する際にcaptchaを表示しないようにするオプションです。


### JSXのformにhidden属性の`form-name`フィールドを追加する

フォームの名前をNetlifyに知らせるためのhidden属性のinputを追加します。

次の例であれば、Netlifyのダッシュボードでは、`contact`という名前でフォームを確認することができます。

```react
<input type="hidden" name="form-name" value="contact" />
```

 最終的なコードは以下のようになります。

```react
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Contact</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
  </head>
  <body>

    <!-- A little help for the Netlify bots if you're not using a SSG -->
    <form name="contact" netlify netlify-honeypot="bot-field" hidden>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <textarea name="message"></textarea>
    </form>

    <div id="root"></div>
    <script type="text/babel">

      ReactDOM.render(
        <form name="contact" method="post">
          <input type="hidden" name="form-name" value="contact" />
          <p>
            <label>Your Name: <input type="text" name="name"/></label>
          </p>
          <p>
            <label>Your Email: <input type="email" name="email"/></label>
          </p>
          <p>
            <label>Message: <textarea name="message"></textarea></label>
          </p>
          <p>
            <button type="submit">Send</button>
          </p>
        </form>,
        document.getElementById("root")
      );

    </script>
  </body>
</html>
```

## 自前のサンクスページを実装する

NetlifyのFormsでお問い合わせを投げるとデフォルトのサンクスページが出て来ます。

コレを変えたかったのですが、公式のドキュメントを読むと、`form`の`action`でサンクスページを指定すればいいよ！みたいに書いていたんですが、ダメだったので違う方法で実装しました。

### statefulにする

まず、上記のフォームをstatefulにすると以下のようになります。

```react
<script type="text/babel">

  const encode = (data) => {
    return Object.keys(data)
        .map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
        .join("&");
  }

  class ContactForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = { name: "", email: "", message: "" };
    }

    /* Here’s the juicy bit for posting the form submission */

    handleSubmit = e => {
      fetch("/", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: encode({ "form-name": "contact", ...this.state })
      })
        .then(() => alert("Success!"))
        .catch(error => alert(error));

      e.preventDefault();
    };

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

    render() {
      const { name, email, message } = this.state;
      return (
        <form onSubmit={this.handleSubmit}>
          <p>
            <label>
              Your Name: <input type="text" name="name" value={name} onChange={this.handleChange} />
            </label>
          </p>
          <p>
            <label>
              Your Email: <input type="email" name="email" value={email} onChange={this.handleChange} />
            </label>
          </p>
          <p>
            <label>
              Message: <textarea name="message" value={message} onChange={this.handleChange} />
            </label>
          </p>
          <p>
            <button type="submit">Send</button>
          </p>
        </form>
      );
    }
  }

  ReactDOM.render(<ContactForm />, document.getElementById("root"));

</script>
```

### サンクスページへ遷移させる

`react-router`を使いました。

適当にサンクスページを作って、パスを`/thanks`のようにします。

ここで、`alert("Success!")`の部分がsubmit成功時に呼ばれるところなんですが、ここで`/thanks`に飛ばしてあげます。

`history`を使ってサンクスページに飛ばすようにしたところ、上手く動きました。
`withRouter`でラップするのも忘れないようにしてください。

```react
import { withRouter } from 'react-router';
.
.
.

fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: encode({ "form-name": "contact", ...this.state })
    })
      .then(() => this.props.history.push('/thanks'))
      .catch(error => alert(error));

.
.
.

export default withRouter(Component);
```

以上になります、もっといい方法があるよ！という方いらっしゃったら是非コメント頂きたいです。