フロントエンド開発といえば。
react アプリの初期化( npm init vite@latest <アプリ名> )

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)

● フォーム の部品を設置する

フォーム の部品の作り方には2つ方法があります
引用 : react-hook-formでregisterとControllerのどちらを使うか - mrsekut-p

・useForm の registerを使って、<input {...register('hoge')} />とする (input , text , MUIの TextField に使用できます )
・useForm のcontrolと <Controller name='hoge' control={フォーム部品コンポーネント }> を使う (上記以外の複雑なコンポーネントはこちらを使用します)

例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. コンポーネントは何を使用するか?

以下のコンポーネントがおすすめです。

・ shadcn/ui

https://ui.shadcn.com/docs/components/input

・ MUI

https://mui.com/material-ui/react-text-field/

・ chakra-ui

https://chakra-ui.com/docs/components

・ radix

https://www.radix-ui.com/

● 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)で非同期で取得したデータをフォームのデフォルト値として流し込む

・ valuesメソッドが使えるようになったようです

https://github.com/react-hook-form/react-hook-form/pull/9261

外部からフォームの値を変更するには以下の2つの方法を使用します。
( なお、useState は使用できません )
( MUI を使用している場合でも問題なくこちらの方法で値を流し込むことができます。(ただしバージョンが古いとうまく動作しないのでできるだけ新しいバージョンにアップデートしましょう。 過去にreact-hook-form@7.3.0 ではうまく動作しませんでした。)

・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でカスタムバリデーション

自作関数を使う場合このように記述できます

function myPasswordCheck(){
 return false
}
.test(
  "mypassword",
  "パスワードが間違っています",
  myPasswordCheck
)

● Yupでバリデーションの実行順序を指定するのは無理?

https://github.com/jquense/yup/issues/503

● Yupで動的にバリデーションエラーメッセージを変更する

createError() を返せばokです。createErrorファンクション自体は渡してあげる必要があります。

    return createError({
      message: `パスワードは半角英字、数字、記号を組み合わせた 8文字以上で入力してください (${password})`,
    });

● 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

● React Hook Form をフォームの外のボタンのメソッドから submit する

onClick = { () => {
    handleSubmit(onSubmit)()
}}

● React Hook Form をフォームの外のボタンのメソッドから submit する (throttleで連打防止)

https://github.com/alibaba/hooks

import { useThrottleFn } from 'ahooks'

  const { run: throttledHandleSubmit } = useThrottleFn(
    () => {
      handleSubmit(onSubmit)()
    },
    { wait: 2000 }
  )
onClick = { throttledHandleSubmit }

● watchでフォームの値を監視する

https://zenn.dev/hirof1990/scraps/2cbf610f439283

No.2068
08/03 14:07

edit