Help us understand the problem. What is going on with this article?

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

エイチームライフスタイル アドベントカレンダー2019、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/
日本語訳も用意されてる! UI/UXもイケてるサイトです)
↓↓公式サイトのスクショ↓↓
スクリーンショット 2019-12-08 23.38.17.png
hook は React16.8 で追加された新機能で、classを書かずにstateが扱えるようになりました。

必見!!Examples

公式サイト内APIでも利用したいメソットごとにサンプルが用意されています。
GitHub README にあるExamplesもオススメ。
こんなことやりたい!が見つかるし、 色々触って動作確認出来ます。

さっそくReact-Hook-FormをInstall :heart_eyes: :heart_eyes:

$ yarn add react-hook-form

作ってみたフォーム

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

こんなフォーム バリデートっ 入力内容確認画面
スクリーンショット 2019-12-14 9.22.29.png スクリーンショット 2019-12-14 12.47.52.png スクリーンショット 2019-12-14 12.50.11.png

コード解説

入力画面コンポーネント Contact.js

公式サイトでuseFormのメソッドが詳しく解説されています。

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

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に比べてレンダリングやマウントにかかる時間が大幅に削減されていることです。

↓↓公式サイトのスクショ↓↓
スクリーンショット 2019-12-14 11.53.57.png

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

最後に

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

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

参考

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away