react-hook-formで値を確認したいときはwatchを使え


課題

以下のgifのように1つ目の質問に応じて2つ目移行の質問内容を変更するフォームを実装したいときにどうすれば良いのかに数時間費やしたので解決方法を残しておきます。

1つ目の質問だけ変更時にsetStateで管理すれば良いのですが、react-hook-formを使うとonChangeイベントが重複してしまう&同じ値を複数の箇所で管理することになってしまうためreact-hook-form側で管理する方法がないか調べていた、というのが今回の背景です。

結論

タイトルで言っちゃってます。watchを使います。
watchでは名前の通り、指定された入力を監視してその値を返します。

以下ではドキュメント内に書かれているサンプルを記載しています

sample.tsx
import React from "react";
import { useForm } from "react-hook-form";

interface IFormInputs {
  name: string
  showAge: boolean
  age: number
}

function App() {
  const { register, watch, formState: { errors }, handleSubmit } = useForm<IFormInputs>();
  const watchShowAge = watch("showAge", false); // you can supply default value as second argument
  const watchAllFields = watch(); // when pass nothing as argument, you are watching everything
  const watchFields = watch(["showAge", "age"]); // you can also target specific fields by their names

  // Callback version of watch.  It's your responsibility to unsubscribe when done.
  React.useEffect(() => {
    const subscription = watch((value, { name, type }) => console.log(value, name, type));
    return () => subscription.unsubscribe();
  }, [watch]);

  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input {...register("name", { required: true, maxLength: 50 })} />
        <input type="checkbox" {...register("showAge")} />
        {/* based on yes selection to display Age Input*/}
        {watchShowAge && (
          <input type="number" {...register("age", { min: 50 })} />
        )}
        <input type="submit" />
      </form>
    </>
  );
}

サンプルコード

課題で載せた動画のサンプルコードはこちら
https://gist.github.com/mu-suke/726638070c94ead6a779be037267ad94

感想

getValuesでできないかと調べていたところ偶然watchの方を見たのでまさに灯台下暗しという気持ちでした。
英語のドキュメントに対する苦手意識が少し残っているので臆せずに読んでいこうと思います。

参考記事