React-Hook-Formがイケてるって聞いたので触ってみた

[エイチームライフスタイル アドベントカレンダー2019](https://qiita.com/advent-calendar/2019/ateam-lifestyle)、18日目は株式会社エイチームライフスタイル、フロントエンドデザイナ@seiraが担当します。


最近はReact, Rail, Sass, php, jQuery などを触っています。

# まずはReact-Hook-Formを知る
github: https://github.com/react-hook-form/react-hook-form
公式サイト: https://react-hook-form.com/
（[日本語訳](https://react-hook-form.com/jp/)も用意されてる！ UI/UXもイケてるサイトです）
↓↓公式サイトのスクショ↓↓
<img width="739" alt="スクリーンショット 2019-12-08 23.38.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/608254a8-a38e-ba03-565f-9e6739e2628c.png">
hook は React16.8 で追加された新機能で、classを書かずにstateが扱えるようになりました。
## 必見！！Examples
公式サイト内[API](https://react-hook-form.com/jp/api/#useForm)でも利用したいメソットごとにサンプルが用意されています。
GitHub README にある[Examples](https://github.com/react-hook-form/react-hook-form/tree/master/examples#examples)もオススメ。
こんなことやりたい！が見つかるし、 色々触って動作確認出来ます。


## さっそくReact-Hook-FormをInstall :heart_eyes: :heart_eyes:
`$ yarn add react-hook-form`

# 作ってみたフォーム
入力画面コンポーネント Contact.js と入力内容確認画面コンポーネント Confirm.js を作成。
入力内容確認画面は、submitボタンを押した時に表示され、表示箇所までにゅるっと移動する仕組み。

| こんなフォーム|バリデートっ|入力内容確認画面|
|:-----------------:|:------------------:|:------------------:|
|<img width="677" alt="スクリーンショット 2019-12-14 9.22.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/dd265e80-996d-6678-67aa-54da0e1407d5.png">|<img width="659" alt="スクリーンショット 2019-12-14 12.47.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/5f74065c-f790-116b-cc23-b316a882c4df.png">|<img width="636" alt="スクリーンショット 2019-12-14 12.50.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/98ee33c8-d373-b096-54af-6c0e511afffa.png">
|

## コード解説

### 入力画面コンポーネント Contact.js
公式サイトで[useFormのメソッド](https://react-hook-form.com/api/#useForm)が詳しく解説されています。

```react:Contact.js
import React, {useState} from 'react'
//stateを持った値と、それを更新するための関数を返したい
import {Element, scroller} from 'react-scroll'
//任意のターゲットに移動させたい
import useForm from 'react-hook-form'
//React-Hook-Formをimport
import Confirmation from './Confirmation'
//入力内容確認画面を表示させるcomponent
import './index.css'
//今回スタイリングは別ファイルで

const Contact = () => {
  const {register, handleSubmit, watch, reset, errors, getValues} = useForm()
  //useFormを呼び出して使いたいメソッドを書く
  const [isConfirmationVisible, setIsConfirmationVisible] = useState(false)
  //isConfirmationVisibleにstateを持たせて、入力内容確認画面の表示・非表示をコントロール
  //isConfirmationVisibleの初期値はfalseで入力内容確認画面は非表示に
  const hideConfirmation = () => setIsConfirmationVisible(false)
  //入力内容確認画面の閉じるボタンを押した時非表示にする関数を宣言
  const onSubmitData = () => setIsConfirmationVisible(true)
  //submitボタンを押した時、入力内容確認画面を表示させる
  const scrollToTarget = () => {
  //にゅるっと移動させる関数を宣言
    scroller.scrollTo('scrollTarget', {
      duration: 800,
      delay: 0,
      smooth: 'easeOutQuint'
    })
  }

  const Department = watch('department')
  const Name = watch('name')
  const Email = watch('email')
  const Contact = watch('contact')
  //watchに各フォーム部品のnameの値を引数で渡すとでタイムリーで入力状態を監視してる

  return(
    <>
      <h2>Contact</h2>
      <form onSubmit={handleSubmit(onSubmitData)} className='contactBox'>
      {/*onSubmit(入力フォームの送信ボタンがクリックされた時に発生するイベント)で入力された値をhandleSubmitで取り出す*/}
        <label htmlFor='department'>所属部署
          <span className={`requiredIcon ${Department ? 'is-ok' : 'is-required'}`}>
          {Department ? 'OK' :'必須'}
          </span>
        </label>
        <select name='department' ref={register({ required: true })}>{/*入力必須項目*/}
          <option value='' hidden>所属部署を選択</option>
          <option value='クリエイティブ戦略部'>クリエイティブ戦略部</option>
          <option value='名古屋開発部'>名古屋開発部</option>
          <option value='営業部'>営業部</option>
          <option value='マーケティング部'>マーケティング部</option>
        </select>
        {errors.department && <p className='formError'>所属部署を選択してください</p>}{/*validationが通らなければerrorがtrueになる*/}
        <label htmlFor='name'>氏名
          <span className={`requiredIcon ${Name ? 'is-ok' : 'is-required'}`}>{/*nameが入力されてたらtrue*/}
            {Name ? 'OK' :'必須'}
          </span>
        </label>
        <input
          name='name'
          placeholder='氏名を入力'
          ref={register({required: true})} />
          {errors.name && <p className='formError'>名字を入力して下さい</p>}{/*nameが正しく入力されていない時に表示される*/}
        <label htmlFor='email'>メールアドレス
          <span className={`requiredIcon ${Email && !errors.email ? 'is-ok': 'is-required'}`}>
            {Email && !errors.email ? 'OK' :'必須' }
          </span>
        </label>
        <input
          type='email'
          name='email'
          placeholder='メールアドレスを入力'
          ref={register({
          required: true,
          value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
          })}
        />
        {errors.email && <p className='formError'>メールアドレスを正しく入力して下さい</p>}
        <label htmlFor='contact'>問い合わせ内容
          <span className={`requiredIcon ${Contact && !errors.contact ? 'is-ok': 'is-required'}`}>
            {Contact && !errors.contact ? 'OK' :'必須' }
          </span>
        </label>
        <textarea
          name='contact'
          placeholder='問い合わせ内容を入力'
          ref={register({
              required: true,
              rows: 8,
              minLength: 1,
              maxLength: 300
            })}
        />
        {errors.contact && <p className='formError'>問い合わせ内容を300文字以内で入力して下さい</p>}
        <div className='btnBox'>
          <input
            type='button'
            onClick={reset}//入力内容もstateもクリア
            value='クリア'
            className='button'/>
          <input
            type='submit'
            value='入力内容を確認'
            onClick={scrollToTarget}//移動先ターゲットにした<Element name='scrollTarget'/>までにゅる移動
            className='button'/>
        </div>
      </form>
      <Element name='scrollTarget'/>
      {isConfirmationVisible &&//trueの時だけ入力内容確認画面を表示
        <Confirmation//入力内容確認画面コンポーネント
          values={getValues()}//getValues()でフォーム全体のデータを返してくれる！！
          hideConfirmation={hideConfirmation}//入力内容確認画面表示・非表示のstateをConfirmationに渡す
        />
      }
    </>
  )
}

export default Contact

```



### 入力内容確認画面コンポーネント Confirm.js
```react:Confirm.js
import React from 'react'
import './index.css'

const Confirmation = props => {
  const {values, hideConfirmation} = props
  //propsで渡ってきたvaluesを受けとって入力内容確認画面で表示
  return(
    <>
      <h2>確認画面</h2>
      <div className='contactBox'>
        <p>所属部署：{values.department}</p>
        <p>氏名：{values.name}</p>
        <p>社員番号：{values.registrationNumber}</p>
        <p>メールアドレス：{values.email}</p>
        <p>携帯電話番号：{values.tel}</p>
        <p>問い合わせ内容：{values.contact}</p>
        <div className='btnBox'>
          <input
            type='button'
            onClick={hideConfirmation}
            //クリックでstateをクリアし、入力内容確認画面コンポーネントを非表示にする
            value='閉じる'
            className='button'/>
        </div>
      </div>
    </>
  )
}

export default Confirmation
```
stateをfunctionalComponentで扱えるようになって、コードが随分シンプルに。

とにかく触ってみたら、なんとなく分かってきた:relaxed:
公式サイトを読み込んで理解を深めたら、もっとシンプルなコードで同じことが出来そう。


何より注目なのは、実装方法がシンプルなだけでなくReduxFormやFormikに比べてレンダリングやマウントにかかる時間が大幅に削減されていることです。

↓↓公式サイトのスクショ↓↓
<img width="1089" alt="スクリーンショット 2019-12-14 11.53.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218656/3ad0dfcc-c6ca-962b-4511-404705e64959.png">

ReduxFormやFormikからReact-Hook-Form載せ替えメリット大ですね！！　
React-Hook-Form 業務でも使ってみたいです。

# 最後に
Ateam Lifestyle Advent Calendar 2019 の 19日目は、@mziyutが投稿します。

"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひ[エイチームグループ採用サイト](https://www.a-tm.co.jp/recruit/)をごらんください。


# 参考
github: https://github.com/react-hook-form/react-hook-form
公式サイト: https://react-hook-form.com/jp/