tag:crieit.net,2005:https://crieit.net/tags/getform.io/feed 「getform.io」の記事 - Crieit Crieitでタグ「getform.io」に投稿された最近の記事 2021-03-09T01:32:10+09:00 https://crieit.net/tags/getform.io/feed tag:crieit.net,2005:PublicArticle/16725 2021-03-09T01:32:10+09:00 2021-03-09T01:32:10+09:00 https://crieit.net/posts/React-Hook-Form-Getform-io React Hook FormとGetform.ioを使って、お問い合わせフォームを作ろう! <p><a target="_blank" rel="nofollow noopener" href="https://react-hook-form.com/jp/">React Hook Form</a>が便利らしいと聞いたので使ってみることにしました。</p> <h2 id="React Hook Form"><a href="#React+Hook+Form">React Hook Form</a></h2> <p>皆さん、<a target="_blank" rel="nofollow noopener" href="https://react-hook-form.com/jp/">React Hook Form</a>を知ってますか?</p> <p>最近トレンドに乗っかってきた、<strong>Form</strong>を<strong>React Hooks</strong>で簡単に作ることのできる代物です。</p> <p><img src="https://i.imgur.com/2dqEW7L.png" alt="img" /></p> <p>特徴として、Hooksを使って簡単にFormが作れる、そして再レンダリングが最小限に抑えられているのでパフォーマンスも高い、らしいです。</p> <h2 id="Getform.io"><a href="#Getform.io">Getform.io</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://getform.io/">Getform.io</a>はフォームのバックエンドを提供するすばらしいサービスです。</p> <p>詳しくは<a target="_blank" rel="nofollow noopener" href="https://blog.tubone-project24.xyz/2021/02/13/netlify-github-action#getformio">こちらの過去記事</a>をご確認いただければと思います。</p> <h2 id="React Hook Form + Getform.io"><a href="#React+Hook+Form+%2B+Getform.io">React Hook Form + Getform.io</a></h2> <p><strong>合体!</strong></p> <p>だめ~となるかと思いましたがうまいことできました。</p> <p><img src="https://i.imgur.com/yYJBK98.jpg" alt="img" /></p> <p>今回はこちらの2技術を使って、お問い合わせフォームを作っていきます。</p> <h2 id="実コード"><a href="#%E5%AE%9F%E3%82%B3%E3%83%BC%E3%83%89">実コード</a></h2> <p>こんな感じのコンポーネントができました。</p> <pre><code class="typescript">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 </code></pre> <h2 id="解説"><a href="#%E8%A7%A3%E8%AA%AC">解説</a></h2> <h3 id="準備"><a href="#%E6%BA%96%E5%82%99">準備</a></h3> <p>まず、フォームの項目に該当するTypeを作ります。</p> <pre><code class="typescript">type Inputs = { name: string, email: string, subject: string, message: string, }; </code></pre> <p>今回は名前、email、題名、メッセージを設定します。</p> <p>次に<strong>React Hook Form</strong>のuseFormを使って<strong>register</strong>などを作っていきます。正直これができれば基本的な機能は8割くらい完成です。</p> <pre><code class="typescript">const { register, handleSubmit, errors } = useForm<Inputs>(); </code></pre> <p>とりあえず用意するのは、formのrefに設定する<strong>register</strong>、onSubmitをコントロールできる<strong>handleSubmit</strong>、requireを検査できる<strong>errors</strong>です。</p> <p>ほかにも、form全体の項目検査のformState.isValidなども使うことができます。</p> <h3 id="Submit"><a href="#Submit">Submit</a></h3> <p>そして肝心な送信(Submit)部分ですがこちらは<a target="_blank" rel="nofollow noopener" href="https://blog.tubone-project24.xyz/2021/02/13/netlify-github-action#getformio">前記事</a>とほぼ同じように<strong>onSubmit</strong>に合わせて処理する関数を用意して、formの<strong>onsubmit属性</strong>に渡してあげればいいだけです。</p> <p>........いいだけですが一つ注意として、渡す際に<strong>handleSubmit</strong>で関数をラップしないと、form情報がうまく取れない、ということです。忘れずに設定してくださいませ。</p> <pre><code class="typescript"> <form onSubmit={handleSubmit(onSubmit)}> </form> </code></pre> <p>またGetform.ioへのPOSTはJSONではなく<strong>mulitpart/form-data</strong>で渡さないと行けないので、FormDataにappendする形でFormのデータを差し込みます。</p> <pre><code class="typescript"> 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()); }); } </code></pre> <p>また、第二引数として、formのeventが取得できます。おそらくEventは<strong>React.BaseSyntheticEvent</strong>だとは思うのですがうまく型が通せなくて悩みだしてしまいましたのでとりあえずanyにしてしまいました。</p> <p>一応、<a target="_blank" rel="nofollow noopener" href="https://github.com/react-hook-form/react-hook-form/discussions/4376">こちら</a>で質問は投げてますが英語がへたくそで誰も答えてくれそうにありませんね。</p> <p>特にeventでデータを取る必要はなさそうですが、たとえば送信時にFormの内容をリセットするなどの処理を書きたいときは</p> <pre><code class="typescript"> e.target.reset(); handleServerResponse(true, "Submitted!"); </code></pre> <p>とやってあげればOKです。</p> <h3 id="Formを書く"><a href="#Form%E3%82%92%E6%9B%B8%E3%81%8F">Formを書く</a></h3> <p>さて、あとは普通のFormを作るようにJSXを書いていきます。</p> <p>唯一違うところはinputやtextareaのref属性にregisterをつけなければいけないですが、それだけで大丈夫です。</p> <pre><code class="typescript"> <textarea name="message" placeholder="Something writing..." rows={6} cols={25} ref={register({required: true })}/> </code></pre> <p>ちなみに、registerのパラメーターで、必須項目やパターンの検査もできます。</p> <pre><code class="typescript"> <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 })} /> </code></pre> <p>また、検査が通っていないときに警告メッセージを出すのはerrorsをつかうことで実現できます。</p> <pre><code class="typescript"> {errors.subject && <span>This field is required</span>} </code></pre> <p>Form部分をすべて実装するとこんな感じです。</p> <pre><code class="typescript"> <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> </code></pre> <p>もう完成です。実に簡単ですね。</p> <p>React Hook Formを使わないと、<a target="_blank" rel="nofollow noopener" href="https://blog.tubone-project24.xyz/2021/02/13/netlify-github-action#getformio">前記事</a>のように、formのonChangeのたびに、setStateしなきゃいけないのですが、すっきり実装できました。</p> <p>React Hook Formを使わないと</p> <pre><code class="typescript"> 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} /> </code></pre> <p>となります。</p> <p>出来上がりはただのFormですのでかっこいいCSSを当ててくださいね。</p> <p><img src="https://i.imgur.com/DsrFLOE.png" alt="img" /></p> <h2 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h2> <p>楽に実装できたので余った時間は担当<strong>ウマ娘</strong>に捧げます。</p> tubone24