投稿日:2024/6/8
更新日:2024/6/8
仕事で割と複雑なformを取り扱うことになったのでその検証、備忘録です。
挙動のサンプル動画を入れたかったのですがmicrocmsのプランによってはできないみたいですね、、
なのでコードだけ置く形になります。
Reactでフォームを効率的に管理するためのライブラリです。最近のReactだとformは全部これ使ってるんじゃないかなと思います。
上記のようなメリットがあります。
React Hook Formをインストールするには、以下のコマンドを使用します。
$ npm install react-hook-form
ここでは、基本的なフォームの作成方法を紹介します。以下のコードは、名前、パスワードの入力フィールドを持つシンプルなフォームです。
"use client";
import { useForm, SubmitHandler } from "react-hook-form";
type Inputs = {
name: string;
password: string;
};
export default function Form() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data);
return (
/* "handleSubmit" は "onSubmit" を呼び出す前に入力値を検証します */
<form onSubmit={handleSubmit(onSubmit)}>
{/* "register" 関数を呼び出してフックに入力を登録します */}
<input defaultValue="test" {...register("name")} />
{/* 必須や他の標準HTML検証ルールを含めて検証を行います */}
<input {...register("password", { required: true })} />
{/* 検証が失敗したときにエラーを返します */}
{errors.password && <span>このフィールドは必須です</span>}
<input type="submit" />
</form>
);
}
基本的な使い方です。
<input defaultValue="test" {...register("name")} />
...register("name")ここに入るのがパラメータ、defaultValue="test" ここに入るのがデフォルトの値です。
上記のフィールドから投げた値は、以下のようにデータを飛ばせます。
{ name: "test" }
バリデーションは以下のように実施できます。
`errors`オブジェクトを使って、特定のフィールドのエラーを確認し、メッセージを表示します。
{errors.password && <span>このフィールドは必須です</span>}
ラジオボタンで選ばれているボタンの値によって値を変更したいみたいなケースがあるかと思います。
選ばれている性別によって、色をリアルタイムで変更してみましょう。
今回は男が選ばれていればblue, 女が選ばれていればredを設定してみます。
watch, useWatch, onChangeを使ったスキーマがあるのでそれぞれ書いておきます。
'use client'
import { useEffect } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
type Inputs = {
color: string
gender: string
}
export default function Form() {
const {
register,
handleSubmit,
setValue,
watch,
formState: { errors },
} = useForm<Inputs>()
const gender = watch('gender')
useEffect(() => {
if (gender === '0') {
setValue('color', 'blue')
} else if (gender === '1') {
setValue('color', 'red')
}
}, [gender])
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
<input
type="radio"
value={0}
{...register('gender', { required: true })}
/>
men
</label>
<label>
<input
type="radio"
value={1}
{...register('gender', { required: true })}
/>
women
</label>
<br />
<input
{...register('color', { required: true })}
defaultValue={'white'}
/>
<input type="submit" />
</form>
)
}
const gender = watch("gender");
watchでregisterに設定しているgenderの部分の変更を監視します。
例えばラジオボタンでgernderが変更された時、その値がここに入ってきます。
useEffect(() => {
if (gender === "0") {
setValue("color", "blue");
} else if (gender === "1") {
setValue("color", "red");
}
}, [gender]);
入ってきたgenderの値によってsetValueで更新をかけます。
数値で設定してもstringで飛ぶのでそこだけ注意です。
'use client'
import { SubmitHandler, useForm } from 'react-hook-form'
type Inputs = {
color: string
gender: string
}
export default function Form() {
const {
register,
handleSubmit,
setValue,
getValues,
formState: { errors },
} = useForm<Inputs>()
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log(data)
}
const handleGenderChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const gender = e.target.value
if (gender === '0') {
setValue('color', 'blue')
} else if (gender === '1') {
setValue('color', 'red')
}
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
<input
type="radio"
value="0"
{...register('gender', {
required: true,
onChange: handleGenderChange,
})}
/>
men
</label>
<label>
<input
type="radio"
value="1"
{...register('gender', {
required: true,
onChange: handleGenderChange,
})}
/>
women
</label>
<input
{...register('color', { required: true })}
defaultValue={'white'}
/>
<input type="submit" />
</form>
)
}
'use client'
import { useEffect } from 'react'
import { SubmitHandler, useForm, useWatch } from 'react-hook-form'
type Inputs = {
color: string
gender: string
}
export default function Form() {
const {
register,
handleSubmit,
setValue,
control,
formState: { errors },
} = useForm<Inputs>()
const gender = useWatch({
control,
name: 'gender',
})
useEffect(() => {
if (gender === '0') {
setValue('color', 'blue')
} else if (gender === '1') {
setValue('color', 'red')
}
}, [gender, setValue])
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
<input
type="radio"
value={0}
{...register('gender', { required: true })}
/>
men
</label>
<label>
<input
type="radio"
value={1}
{...register('gender', { required: true })}
/>
women
</label>
<br />
<input {...register('color', { required: true })} defaultValue="white" />
<input type="submit" />
</form>
)
}
useFieldArrayを利用することで実装できます。
rails viewで言うcocoon的なやつです。
'use client'
import { SubmitHandler, useFieldArray, useForm } from 'react-hook-form'
type Inputs = {
dynamicFields: { value: string }[]
}
export default function Form() {
const {
register,
handleSubmit,
control,
formState: { errors },
} = useForm<Inputs>({
mode: 'onChange',
defaultValues: {
dynamicFields: [{ value: '' }],
},
})
const { fields, append, remove } = useFieldArray({
control,
name: 'dynamicFields',
})
const onSubmit: SubmitHandler<Inputs> = (data) => {
console.log(data)
}
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((field, index) => (
<div key={field.id}>
<input
{...register(`dynamicFields.${index}.value` as const, {
required: 'このフィールドは必須です',
})}
/>
{errors.dynamicFields?.[index]?.value && (
<p className="error">
{errors.dynamicFields[index].value.message}
</p>
)}
<button type="button" onClick={() => remove(index)}>
削除
</button>
</div>
))}
<button type="button" onClick={() => append({ value: '' })}>
追加
</button>
<input type="submit" />
</form>
</div>
)
}
fields:現在のフィールド配列を保持するオブジェクト。
append:新しいフィールドを追加する関数。
remove: 既存のフィールドを削除する関数。