😇

Next.js + Netlify Forms + React Hook Formでハマった話

2020/11/30に公開

なぜハマったのか

Next.jsで作られているサイトでNetlify Formsを利用するサンプルが下の記事にありますが、こちらはformタグの onSubmit 属性にコールバックを登録せず、HTMLのみでフォームの送信を行う場合の解説です。
https://www.netlify.com/blog/2020/05/26/add-a-netlify-powered-contact-form-to-your-next.js-site/

ここで厄介なのが、React Hook Formを利用している場合です。React Hook Formはformタグの onSubmit 属性に handleSubmit 関数をコールバックとして登録することで動作します。上記の記事と同様の書き方で onSubmit 属性を使ったまま書いてみましたが、Netlify側でフォームを認識してくれず、Netlify Formsとの連携がうまくできませんでした。

すでにReact Hook Formを使ってバリデーションを組んでしまっていたので、なんとかそのままの実装で安定してフォームをNetlifyに認識してもらう方法を模索しました。Netlifyがフォームを認識してくれるか否かは手元でサクッと確認できるものではないので、formタグの属性をいじりながら何度もデプロイして確認するのは骨が折れました。

解決方法

  • /public にフォームの項目を一通り書いた静的なHTMLファイルを配置してNetlifyにフォームを認識させる
  • 実際のフォームのコンポーネントでは onSubmit 属性を使ってReact Hook Formのバリデーションをかけ、AxiosやFetch APIでフォームの内容をPOSTする

/public に静的なHTMLファイルを配置

まずはフォームをNetlifyに認識してもらうために、フォームの項目を一通り持っている下記のようなHTMLファイルを作成します。

form.html
<!DOCTYPE html>
<html>
  <body>
    <form name="contact" netlify hidden>
      <input type="text" name="name" />
      <input type="text" name="email" />
      <input type="text" name="subject" />
      <input type="text" name="body" />
    </form>
  </body>
</html>

Netlifyがフォームを認識するためには netlify という属性をformタグにつける必要があります。こちらのファイルを /public フォルダに配置しておくと、 next build を実行したときに /out フォルダに配置され、デプロイの際にNetlifyから見える状態にすることができます。

コンポーネント内のフォームの書き方

次にフォームのコンポーネントの書き方です。

form.js
import { useForm } from 'react-hook-form'

export function Form() {
  const { register, handleSubmit, errors } = useForm()
  const onSubmit = (data) => {
    data['form-name'] = 'contact'
    axios.post('/', new URLSearchParams(data))
      .then(postSubmission)
      .catch(handleError)
  }
  
  return (
    <form onSubmit={handleSubmit(onSubmit)} >
      <input type="text" ref={register({ required: '必須項目です' })}>
      ...
      <button type="submit">送信</button>
    </form>
  )
}

register handleSubmit errors の3つをReact Hook Formの useForm フックから取得しています。React Hook Formの使い方やインストール方法は公式ページをご覧ください。

最大のポイントは、 handleSubmit 関数の引数となるコールバック関数 onSubmit です。この関数の引数 data はフォームデータのオブジェクトです。こちらの関数内で、Netlifyがフォームの名前として利用する form-name というキーを data に追加します。 form-name の値は /public に配置したHTMLファイル内のformタグの name 属性の値と一致させます。最後に onSubmit 関数内でAxiosを利用してフォームデータの送信を行うようにします。

確認

Netlifyへのデプロイが完了した時点で、Netlifyの管理画面のFormsページに、上で作成したHTMLファイルのformタグの name 属性として指定した名前のフォームができていれば、HTMLファイルが正常に認識されています。実際にフォームを送信してみて、データがNetlifyに登録されるか確認してみてください。

もっと良い方法をご存じの方、ぜひぜひご指導ご教示いただけますと幸いです。

参考

https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/#form-handling-with-a-stateless-react-form
https://www.netlify.com/blog/2020/05/26/add-a-netlify-powered-contact-form-to-your-next.js-site/
https://qiita.com/nanaki14/items/007eae905d6305f75f6a

Discussion