React Hook Formでフォームを作成する ( + Yup )

● 1. React Hook Formのインストール

npm install react-hook-form
または
yarn add  react-hook-form

● 2. React Hook Formを使用する

import { useForm } from 'react-hook-form';
    // React Hook Form
    const {
        register,
        handleSubmit,
        formState: { errors },
    } = useForm();

  const onSubmit = (data:any) => {alert('form送信されました'); console.log(data);}

useForm() には様々なオプションを渡すことができます

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

useForm({
  mode: 'onSubmit',  // onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'
  reValidateMode: 'onChange',
  defaultValues: {},
  resolver: undefined,
  context: undefined,
  criteriaMode: "firstError",
  shouldFocusError: true,
  shouldUnregister: false,
  shouldUseNativeValidation: false,
  delayError: undefined
})

useForm() して返ってくる handleSubmitに (バリデーションOKの時の関数,バリデーションNGの時の関数) を渡します

handleSubmit(SubmitHandler, SubmitErrorHandler)

例1: 会社名を入力するフォームに「入力必須」「入力文字数4文字以上」のバリデーションを設定します

return (
    <form onSubmit={handleSubmit(onSubmit)}>
        <input {...register('company', { required: true, minLength: 4 })} placeholder="株式会社○○" />
        {errors.company?.type === "required" && <div className="err_message" id="company_err">会社名は必須です</div>}
        {errors.company?.type === "minLength" && <div className="err_message" id="company_err">会社名は4文字以上を入力してください</div>}

        <input type="submit" />
    </form>
);

例2: 次のように「チェック内容」と「エラーメッセージ」をまとめて記述することもできます

    <input type="email" placeholder="user@server.xxx"
        {...register("email", {
            required: "入力必須です",
            pattern: {
            value: /\S+@\S+\.\S+/,
            message: "メールアドレスが不完全です"
            }
        })}
    />
    {errors.email && <div className="err_message">{errors.email.message}</div>}

● 3. フォーム入力値のバリデーションの記述と種類

https://react-hook-form.com/jp/api#register

● 4. React Hook Form + Yup を使用する

バリデーションの記述をYupでまとめるには次のように記述します

npm install @hookform/resolvers yup
または
yarn add @hookform/resolvers yup
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
  const validationSchema = Yup.object().shape({
    firstName: Yup.string()
      .required('First Name は必須です'),
    lastName: Yup.string()
      .required('Last Name は必須です'),
  });

  const formOptions = { resolver: yupResolver(validationSchema) };

  const { register, handleSubmit, reset, formState } = useForm(formOptions);
  const { errors } = formState;
<form onSubmit={handleSubmit(onSubmit)}>
  <input type="text" {...register('firstName')} className={`form-control ${errors.firstName ? 'is-invalid' : ''}`} />
  <div className="invalid-feedback">{errors.firstName?.message}</div>
</form>

name="firstName" は記述しなくてokです。

● SWR(Axios)で非同期で取得したデータをフォームのデフォルト値として流し込む

外部からフォームの値を変更するには以下の2つの方法を使用します。
( なお、useState は使用できません )

・A. Resetメソッドを使う方法

次の二箇所にデータを流し込む命令をセットします

// ● SWR
const fetcher = (url: string) => axios(url)
  .then((res) => {
    reset(res.data);  // 1. axiosでデータ取得時にデータをフォームに反映(Re-render)
    return res.data
  });
const { data, error, mutate } = useSWR(`http://localhost:8000/api/news/1`, fetcher);

// ● useForm
const formOptions = {
  defaultValues: data,  // 2. SWRのキャッシュがデータをすでに取得している場合はキャッシュからフォームに反映
};
const { control, register, handleSubmit, reset, formState: { errors } } = useForm(formOptions);

・B. setValueメソッドを使う方法

  // ● React Hook Form
  const { register, handleSubmit, setValue, formState } = useForm(formOptions);

setValueを使用します

 // ● SWR
  const fetcher = (url: string) => axios(url)
    .then((res) => {
      // 最初に1度だけフォームに値をセット
      const data = res.data;
      Object.keys(data).forEach(function (k) {
          setValue(k, data[k]);
      });

      return res.data
    });

  const { data, error, mutate } = useSWR(`http://localhost:8000/api/news/${params.id}`, fetcher);
  // swrによるfetchエラー時
  if (error) return <div>failed to load</div>

● setValue() がうまく動作しない場合は

{...register('hoge')} の記述が抜けている可能性がありますチェックしましょう。

<input type="text" id="hoge" {...register('hoge')} />

● Yupでカスタムバリデーション

let customValidation = yup.object().shape({
  beverage: yup
    .string()
    .test("is-tea", "${path} is not tea", value => value === "tea")
});

● useFormState

https://react-hook-form.com/api/useformstate
https://qiita.com/bluebill1049/items/f838bae7f3ed29e81fff

● @hookform/devtools

yarn add @hookform/devtools
import { DevTool } from '@hookform/devtools';

jsx

<DevTool control={control} placement="top-left" />

<form onSubmit={handleSubmit(onSubmit, onError)}>
.............
</form>

● その他参考になるサイト

公式サンプル
https://github.com/react-hook-form/react-hook-form/tree/master/examples

https://bit.ly/3oSjgm9

React Hook Form 7 - Form Validation Example | Jason Watmore's Blog

jsonによるスキーマ定義をyupに変換する

https://github.com/kristianmandrup/schema-to-yup

yarn add schema-to-yup
No.2068
06/28 19:05

edit