【React】React Hook Formを使ってリファクタリングしてみる

# はじめに

とある日のこと

私「いやー、仕様変更に伴ってフォームの項目が増えてきましたね...」

メンバー「各項目に対して状態変数を宣言しているのでコード量がすごいことになってます」

私「どうにかして上手く書けないですかね」

メンバー「なんかReact Hook Formというのを使えば上手く書けるみたいですね」

私「ちょっと触ってみますか」

# 本題

React Hook Formとはフォームの状態管理やバリデーションを作成することができるライブラリです。

https://react-hook-form.com/

https://chakra-ui.com/getting-started/with-hook-form

# React Hook Formを使わない場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/084b75bb-0efb-1d1f-1d92-8fe8bf4afc51.png)

<details>
  <summary>コードを表示する</summary>

```tsx
import { FormLabel, Input, Button, VStack, Text } from '@chakra-ui/react'
import React, { ChangeEvent, useState } from 'react' 

function App() {
 const [name, setName] = useState("")
 const [nameErr, setNameErr] = useState("")
 const [isNameErr, setIsNameErr] = useState(false)

 const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  console.log(name)
 }
 
 const onChangeName = (e: ChangeEvent<HTMLInputElement>) => {
  if (e.target.value.length === 0) {
    setNameErr("名前は必須です");
    setIsNameErr(true);
  } else {
    setNameErr("");
    setIsNameErr(false);
  }
  setName(e.target.value)
 }

 return (
   <VStack>
     <form  onSubmit={(e) => handleSubmit(e)}>
       <VStack>
         <FormLabel htmlFor="名前">First name</FormLabel>
         <Input id="name" type='text' onChange={(e) => onChangeName(e)} isInvalid={isNameErr}/>
         {isNameErr && <Text color="red.500">{nameErr}</Text>}
         <Button mt={4} colorScheme="teal" type="submit" isDisabled={!(name)}>
           Submit
         </Button>
       </VStack>
     </form>
   </VStack>
 )
}
export default App;

```
</details>

コードでは状態変数や関数をたくさん宣言していますね…
「名前」の状態変数、エラーメッセージの状態変数、バリデートの状態変数の3つを宣言しています。
また、状態によってエラーメッセージを表示させる関数も宣言しています。
入力フォームに新しく項目を追加する度にこれらを宣言していくと凄いことになります。

そこでReact Hook Formを使ってみたいと思います

# React Hook Formを使った場合

<details>
  <summary>コードを表示する</summary>

```tsx
import { FormLabel, Input, Button, VStack, Text } from '@chakra-ui/react'
import { useForm } from 'react-hook-form';
 
interface LoginForm {
  name: string;
  password: string;
}

function App() {
const { register, handleSubmit, formState: {errors},} = useForm<LoginForm>({mode: "onChange"});
 
const onSubmit = (data: LoginForm) => {
  console.log(data)
}
 return (
   <VStack>
     <form onSubmit={handleSubmit(onSubmit)}>
       <VStack>
         <FormLabel htmlFor="名前">First name</FormLabel>
         <Input id="name" type='text' {...register("name", {required: "名前は必須です", minLength: {value: 4, message: "4文字以上で入力してください"}})} />
         <Text color="red.500">{errors.name?.message as React.ReactNode}</Text>
         <Button mt={4} colorScheme="teal" type="submit">
           Submit
         </Button>
       </VStack>
     </form>
   </VStack>
 )
}
export default App;

```

</details>

コードが減ったのでスリムになりました。
バリデーションは名前が空の場合の条件と4文字以上の条件にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/8c930e67-ee6a-29ef-92a4-1c3c80d40032.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/cd8717eb-4a79-29a0-2a17-430bfacb1768.png)

4文字以上のバリデーションは文字を入力しているタイミングでチェックしてくれるようにしています。
これはuseFormの`mode: "onChange"`で指定することができます。

また、バリデートエラーが発生している際はボタンを押してもデータが送信されないようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/8405c7e6-a5f9-341d-027b-40f4ed2d99f2.png)


# おわりに
リファクタリングが捗りそうです

# 参考
https://www.youtube.com/watch?v=f1fysEKNwQA