React Hook Formで画像のアップロードを実装する方法

はじめに

この記事では、React Hook Formを使用して、画像のアップロード機能を実装する方法を解説します。

コードの完成形

まず、以下のコードをご覧ください。

import { ChangeEvent, useCallback, useRef } from "react";
import { useForm, SubmitHandler } from "react-hook-form";

type Input = {
  image: File | null
}

export default function Form() {
  const fileRef = useRef(null);
  const {
    handleSubmit,
    setValue,
    formState: { isDirty },
  } = useForm<Input>({
    defaultValues: {
      image: null
    }
  });

  const onSubmit: SubmitHandler<Input> = ({ image }) => {
    console.log(image, isDirty);
  }

    const handleChange = useCallback(async (e: ChangeEvent<HTMLInputElement>) => {
      const { files } = e.currentTarget;
      if (!files?.length) return;

      setValue('image', files?.[0], { shouldDirty: true });
      e.target.value = '';
    }, [setValue]);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="file"
        ref={fileRef}
        name="image"
        accept="image/png, image/jpeg"
        onChange={handleChange}
        data-testid="fileInput"
      />
      <input type="submit" value="submit" />
    </form>
  );
}

全体としてはこのように実装します。

下記にてポイントを解説していきます。

解説

型定義

type Input = {
  image: File | null
}

Inputという型を定義して、画像をFileまたはnullで受け取るようにしています。

useFormの使用

  const {
    handleSubmit,
    setValue,
    formState: { isDirty },
  } = useForm<Input>({
    defaultValues: {
      image: null
    }
  });

setValueはinput要素でFileを受け取った際にReact Hook Formへ値を渡すために利用します。これによりフォームの状態を管理します。

実際にはformStateのisDirtyは必須ではないですが使用することにより入力値の変更を検知できるためオススメです。

ファイルの変更をハンドリングする

    const handleChange = useCallback(async (e: ChangeEvent<HTMLInputElement>) => {
      const { files } = e.currentTarget;
      if (!files?.length) return;

      setValue('image', files?.[0], { shouldDirty: true });
      e.target.value = '';
    }, [setValue]);

上記のようにChangeイベントをハンドリングします。

ここでsetValueを使用する際にファイルを渡しますがshouldDirtyオプションをtrueにすることでisDirtyの状態をチェックすることができます。

まとめ

このように画像のアップロード機能を実装することができました。特にisDirty関連は私自身少しハマったポイントでしたので備忘録も兼ねて記載しました。

少しでも困っている人の参考になればと思います。