👀

React Hook FormのuseWatch()で再レンダリングを分離する

2024/03/04に公開

React Hook Form では 内部で非制御コンポーネントが採用されており基本的には不必要な再レンダリングは抑えられているのがメリットです。しかし、使い方によってはそのメリットを崩してしまうことがあります。

useFormの戻り値に、watch()という関数があります。
watch()は指定された入力を監視し、その値を返します。また、フォームを監視して変更があるたびにフォーム全体の再レンダリングをトリガーするため、フィールド数が多いフォームでの使用はなるべく避けたい実装になります。

useWatch()を使用してカスタムフックレベルで再レンダリングを分離する

useWatchを使用してカスタムフックレベルで再レンダリングを分離することで必要な範囲だけ再描画させることができます。

以下はwatch()useWatch()それぞれを使用した場合を比較するためのサンプルです。

type FormInputs = {
  firstName: string;
  lastName: string;
};

const FirstNameField = () => {
  const { watch, register } = useFormContext();
  // watchを使った場合: <Root /> が再描画される
  const firstName = watch("firstName");
  return (
    <>
      <input {...register("firstName")} placeholder="firstName" />
      <p>{firstName}</p>
    </>
  );
};

const LastNameField = () => {
  const { register } = useFormContext();
  // useWatchを使った場合: 再描画されるのはこのコンポーネントだけ
  const lastName = useWatch({ name: "lastName" });
  return (
    <>
      <input {...register("lastName")} placeholder="lastName" />
      <p>{lastName}</p>
    </>
  );
};

export const Root = () => {
  const methods = useForm<FormInputs>();
  return (
    <FormProvider {...methods}>
      <FirstNameField />
      <LastNameField />
    </FormProvider>
  );
};

watch()useWatch()の違いは、再レンダリングを引き起こす範囲になります。

  • watch()はuseFormを呼び出している箇所から再レンダリングされる。(useFormContextを使用した場合も同様)
  • useWatch()はカスタムフックを定義したコンポーネントでのみ再レンダリングされる

watch()を使用した場合のレンダリングハイライト

watch()を使用している<FirstNameField />は、
firstNameが変更されるたびにフォーム全体の<Root />が再レンダリングされています。

useWatch()を使用した場合のレンダリングハイライト

useWatch()を使用している<LastNameField />は、
lastNameを変更した時に<LastNameField />のみ、再レンダリングされていることがわかります。

参考

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

https://react-hook-form.com/docs/useform/watch

frontend flat

Discussion