この記事は1625文字約5分で読めます

React Hook Formが便利らしいと聞いたので使ってみることにしました。

Table of Contents

React Hook Form

皆さん、React Hook Formを知ってますか?

最近トレンドに乗っかってきた、FormReact Hooksで簡単に作ることのできる代物です。

img

特徴として、Hooksを使って簡単にFormが作れる、そして再レンダリングが最小限に抑えられているのでパフォーマンスも高い、らしいです。

Getform.io

Getform.ioはフォームのバックエンドを提供するすばらしいサービスです。

詳しくはこちらの過去記事をご確認いただければと思います。

React Hook Form + Getform.io

合体!

img

だめ~となるかと思いましたがうまいことできました。

img

今回はこちらの2技術を使って、お問い合わせフォームを作っていきます。

実コード

こんな感じのコンポーネントができました。

import React, {useState} from "react";
import { useForm } from "react-hook-form";
import Button from "./button";
type Inputs = {
  name: string,
  email: string,
  subject: string,
  message: string,
};

const ContactForm = (): JSX.Element => {
  const [serverState, setServerState] = useState({ submitting: false, status: {ok: false, msg: ""} });
  const { register, handleSubmit, errors } = useForm<Inputs>();
  const handleServerResponse = (ok: boolean, msg: string) => {
    setServerState({ submitting: true, status: { ok, msg } });
  };
  const onSubmit = (data: Inputs, e: any) => {
    const formData = new FormData();
    formData.append("name", data.name)
    formData.append("email", data.email)
    formData.append("subject", data.subject)
    formData.append("message", data.message)
    fetch('https://getform.io/f/8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', {
      method: 'POST',
      body: formData
    })
      .then(() => {
        e.target.reset();
        handleServerResponse(true, "Submitted!");
      })
      .catch((error) => {
        alert(error)
        console.error(error)
        handleServerResponse(false, error.toString());
      });
  }

  return (
  <form onSubmit={handleSubmit(onSubmit)}>
    <p>
    <label>Your Name<br/>
    <input
      name="name"
      placeholder="Enter your name"
      type="text"
      ref={register({ required: true })} />
    {errors.name && <span>This field is required</span>}
    </label>
    </p>
    <p>
    <label> Your email<br/>
    <input
      name="email"
      type="email"
      placeholder="Enter your email"
      ref={register({ pattern: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i, required: true })} />
    {errors.email && <span>This field is required and only email format</span>}
    </label>
    </p>
    <p>
    <label>
      Subject<br/>
    <input
      name="subject"
      type="text"
      maxLength={30}
      placeholder="Subject here..."
      ref={register({required: true })} />
      {errors.subject && <span>This field is required</span>}
    </label>
    </p>
    <p>
    <label>
      Message<br />
      <textarea
        name="message" placeholder="Something writing..." rows={6} cols={25} ref={register({required: true })}/>
        {errors.message && <span>This field is required</span>}
    </label>
    </p>
      <Button dark={serverState.submitting && serverState.status.ok} disabled={serverState.submitting && serverState.status.ok}>
        { serverState.submitting && serverState.status.ok ? serverState.status.msg: 'Submit'}
      </Button>
  </form>
);
}

export default ContactForm

解説

準備

まず、フォームの項目に該当するTypeを作ります。

type Inputs = {
  name: string,
  email: string,
  subject: string,
  message: string,
};

今回は名前、email、題名、メッセージを設定します。

次にReact Hook FormのuseFormを使ってregisterなどを作っていきます。正直これができれば基本的な機能は8割くらい完成です。

const { register, handleSubmit, errors } = useForm<Inputs>();

とりあえず用意するのは、formのrefに設定するregister、onSubmitをコントロールできるhandleSubmit、requireを検査できるerrorsです。

ほかにも、form全体の項目検査のformState.isValidなども使うことができます。

Submit

そして肝心な送信(Submit)部分ですがこちらは前記事とほぼ同じようにonSubmitに合わせて処理する関数を用意して、formのonsubmit属性に渡してあげればいいだけです。

........いいだけですが一つ注意として、渡す際にhandleSubmitで関数をラップしないと、form情報がうまく取れない、ということです。忘れずに設定してくださいませ。

 <form onSubmit={handleSubmit(onSubmit)}>
 </form>

またGetform.ioへのPOSTはJSONではなくmulitpart/form-dataで渡さないと行けないので、FormDataにappendする形でFormのデータを差し込みます。

  const onSubmit = (data: Inputs, e: any) => {
    const formData = new FormData();
    formData.append("name", data.name)
    formData.append("email", data.email)
    formData.append("subject", data.subject)
    formData.append("message", data.message)
    fetch('https://getform.io/f/8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', {
      method: 'POST',
      body: formData
    })
      .then(() => {
        e.target.reset();
        handleServerResponse(true, "Submitted!");
      })
      .catch((error) => {
        alert(error)
        console.error(error)
        handleServerResponse(false, error.toString());
      });
  }

また、第二引数として、formのeventが取得できます。おそらくEventはReact.BaseSyntheticEventだとは思うのですがうまく型が通せなくて悩みだしてしまいましたのでとりあえずanyにしてしまいました。

一応、こちらで質問は投げてますが英語がへたくそで誰も答えてくれそうにありませんね。

特にeventでデータを取る必要はなさそうですが、たとえば送信時にFormの内容をリセットするなどの処理を書きたいときは

        e.target.reset();
        handleServerResponse(true, "Submitted!");

とやってあげればOKです。

Formを書く

さて、あとは普通のFormを作るようにJSXを書いていきます。

唯一違うところはinputやtextareaのref属性にregisterをつけなければいけないですが、それだけで大丈夫です。

      <textarea
        name="message" placeholder="Something writing..." rows={6} cols={25} ref={register({required: true })}/>

ちなみに、registerのパラメーターで、必須項目やパターンの検査もできます。

    <input
      name="email"
      type="email"
      placeholder="Enter your email"
      ref={register({ pattern: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i, required: true })} />

また、検査が通っていないときに警告メッセージを出すのはerrorsをつかうことで実現できます。

 {errors.subject && <span>This field is required</span>}

Form部分をすべて実装するとこんな感じです。

  <form onSubmit={handleSubmit(onSubmit)}>
    <p>
    <label>Your Name<br/>
    <input
      name="name"
      placeholder="Enter your name"
      type="text"
      ref={register({ required: true })} />
    {errors.name && <span>This field is required</span>}
    </label>
    </p>
    <p>
    <label> Your email<br/>
    <input
      name="email"
      type="email"
      placeholder="Enter your email"
      ref={register({ pattern: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i, required: true })} />
    {errors.email && <span>This field is required and only email format</span>}
    </label>
    </p>
    <p>
    <label>
      Subject<br/>
    <input
      name="subject"
      type="text"
      maxLength={30}
      placeholder="Subject here..."
      ref={register({required: true })} />
      {errors.subject && <span>This field is required</span>}
    </label>
    </p>
    <p>
    <label>
      Message<br />
      <textarea
        name="message" placeholder="Something writing..." rows={6} cols={25} ref={register({required: true })}/>
        {errors.message && <span>This field is required</span>}
    </label>
    </p>
      <Button dark={serverState.submitting && serverState.status.ok} disabled={serverState.submitting && serverState.status.ok}>
        { serverState.submitting && serverState.status.ok ? serverState.status.msg: 'Submit'}
      </Button>
  </form>

もう完成です。実に簡単ですね。

React Hook Formを使わないと、前記事のように、formのonChangeのたびに、setStateしなきゃいけないのですが、すっきり実装できました。

React Hook Formを使わないと

  handleChange(e) {
    this.setState({ [e.target.name]: e.target.value });
  }
  
(中略)
                  <input
                    type="text"
                    name="name"
                    className="form-control"
                    maxLength="30"
                    minLength="2"
                    required
                    placeholder="Enter your name"
                    onChange={this.handleChange}
                  />

となります。

出来上がりはただのFormですのでかっこいいCSSを当ててくださいね。

img

結論

楽に実装できたので余った時間は担当ウマ娘に捧げます。

tubone24にラーメンを食べさせよう!

ぽちっとな↓

Buy me a ramen