🐒

React Hook Formを勉強したばかりの人がやりがちな誤った実装パターン3選

に公開

はじめに

私自身がReactを勉強したてで、バリデーション実装のためにReact Hook Formを勉強した際に経験した誤った実装パターンについて紹介します。

誤った実装パターンで覚えてしまうと読みにくいコードになったり、チーム開発で他のエンジニアに迷惑がかかる可能性があります。実際に私の現場でも、誤った実装が積み重なり大規模なリファクタが必要になったことがありました

React Hook Formとは

Reactでフォームを扱う際、バリデーションや入力管理を楽にしてくれるライブラリが React Hook Formです。公式ドキュメントにも「パフォーマンスが良い」「記述量が減る」と書かれており、人気が高いライブラリの一つです。


公式ドキュメント

Reactを使って入力フォーム実装をする際にはぜひ覚えておきたいライブラリです。

ただし、初めて学んだときに「やってしまいがちな誤った実装」があります。
この記事では、私自身が個人開発やチーム開発で経験した React Hook Formの誤った使い方と改善例 を3つ紹介します。

これから学ぶ人が同じ落とし穴にはまらないように参考にしていただければと思います。

誤った実装パターン1:フォームの値を useState で管理してしまう

❌ 誤った実装例(抜粋)

index.jsx
// useStateで二重管理してしまう例
const [email, setEmail] = useState("");
<input value={email} {...register("email")} onChange={(e) => setEmail(e.target.value)} />

Reactを勉強すると、まず「状態管理=useState」と覚えますよね。
そのため、フォームを実装するときにも「とりあえずuseStateで管理しよう」と考える方が多いと思います。

しかし、React Hook Formを使う場合には注意が必要です。

なぜ問題か?

React Hook Formは内部でフォームの状態をすべて管理してくれます。
registerを使えば、その項目は自動的にReact Hook Formの管理下に入ります。

ここでuseStateを併用すると 二重管理 が発生し、

  • useStateregisterの両方で同じ値を持つため、どちらを正とすべきか分からなくなる
  • コードが煩雑になり、バグを生みやすい
  • 大規模フォームやリファクタ時に大きな負担になる

といった問題が発生します。

では、正しい書き方を見てみましょう。

✅ 改善例(抜粋)

index.tsx
// 不要なonChangeとuseStateを削除
<input type="email" {...register("email")}  />

これで十分です。
React Hook Formが値を管理してくれるので、余計なuseStateは必要ありません。

よくある質問:フォームの値はどう扱うの?

「じゃあ、フォームに入力された値はどうやって使えばいいの?」と思う方もいるでしょう。

React Hook Formでは以下の方法があります:

  • 値を参照したいとき → getValueswatchを使う
  • 値を更新したいとき → setValueを使う

なので、追加でstateを準備する必要はありません。

誤った実装パターン2:setValue を毎回 onChange に書いてしまう

これは私が初めてReact Hook Formを使った時にやってしまった実装です。

当時は「入力値をフォームに反映するならsetValueで書き換えればいい」と思い込んでいました。
そのため、onChangesetValueを直接書き、送信時にはgetValuesで値を取り出す――という遠回りな書き方をしていました。

❌ 誤った実装例(抜粋)

index.tsx
const { getValues, setValue, handleSubmit, register } = useForm();


// 送信時の処理
const onSubmit = () => {
  // ❌ 不要: getValuesで入力内容を取得
  const userName = getValues("username");
  console.log("入力されたユーザー名:", userName);
};

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    {/* ❌ 不要: onChangeで毎回 setValue を呼び出している */}
    <input
      type="text"
      {...register("username")}
      onChange={(e) => setValue("username", e.target.value)}
    />
    <button type="submit">送信</button>
  </form>
);

なぜ問題か?

一見「フォームに入力された値をちゃんと保存できている」ように見えますが、以下の問題があります。

  • setValuegetValuesを毎回使うのは冗長で読みづらい
  • registerが自動でやってくれる処理をわざわざ二重に書いている
  • 複雑なフォームになるとバグの温床になる

React Hook Form の基本思想は、
「入力はregisterに任せ、送信時はhandleSubmitで受け取る」
です。

✅ 改善例(抜粋)

index.tsx
const { handleSubmit, register } = useForm();

const onSubmit = (data) => {
  // ✅ 正しくは handleSubmit の data から受け取れる
  console.log("入力されたユーザー名:", data.username);
};

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    {/* registerを使えば自動で値が管理される */}
    <input type="text" {...register("username")} />
    <button type="submit">送信</button>
  </form>
);

ポイントまとめ

  • setValueは「外部から値をフォームにセットする必要がある場合」にだけ使う
    例: APIで取得したユーザー名を初期表示する
  • 通常のフォーム入力は、registerで十分
  • handleSubmitdata引数を使えば、素直に入力値を受け取れる

つまり、普段のフォームは register + handleSubmit」でシンプルに書ける ということです。

誤った実装パターン3:エラー表示を忘れる / formState.errors を使えない

これは私が初めてバリデーションを試したときにやってしまった失敗です。
register("email", { required: true })と書けば、自動で「必須入力です」というメッセージが出てくれるものだと思い込んでいました。

実際には、エラーメッセージを明示的に書いてあげないとユーザーには何も表示されません。
当時は個人開発だったので、安易にconsole.logalertでエラーを確認して「まあ動いてるしこれでいいか」と済ませてしまっていました。

❌ 誤った実装例(抜粋)

index.tsx
<input {...register("email", { required: true })} />
{/* エラーを表示していない */}

なぜ問題か?

バリデーションを設定しても、ユーザーに「どこで入力が間違っているのか」を伝えなければ意味がありません。
「必須なのに入力しなくても送信できる」と誤解を招いたり、入力ミスに気づかないまま送信してしまう可能性があります。
結果として、ユーザー体験が大きく低下してしまいます。

✅ 改善例(抜粋)

index.tsx
const { register, formState: { errors } } = useForm();

return (
    <>
      <input {...register("email", { required: "メールアドレスは必須です" })} />
      {errors.email && <p>{errors.email.message}</p>}
    </>
)

formState.errorsを活用すれば、どの項目でエラーが起きたのかをユーザーに丁寧に伝えることができます。

フォームは「入力しやすい」だけでなく「間違いに気づきやすい」ことも同じくらい重要です。
React Hook Form のエラーメッセージ表示をしっかり取り入れて、使いやすいフォームを作りましょう。

まとめ

今回は、React Hook Form を勉強したばかりの人がやりがちな誤った実装を3つ紹介しました。

  • フォームの値を useState で管理してしまう
  • setValue を毎回 onChange に書いてしまう
  • エラー表示を忘れる / formState.errors を使えない

私自身がこれらの間違いを犯していた大きな原因は、初心者向けチュートリアルを鵜呑みにして「とりあえず動く」状態で満足してしまったことでした。

フォーム実装で大切なのは、「React Hook Form に任せられる部分は任せる」ことです。余計なコードを足すと保守性が下がり、かえってバグの温床になります。

おすすめの学び方としては、

  • コードを書いたら人に見てもらう
  • 他の人がどう実装しているかを観察する
    ことを意識すると、無駄な実装やクセに気づきやすくなります。

これから React Hook Form を使い始める方は、ぜひ公式ドキュメントも併せてチェックしつつ、今回の内容を参考にしてみてください。

参考リンク

React Hook Form 公式ドキュメント

Discussion