🐶

React Hook FormのControllerを使用した際の<input type='file' />の問題と対処法

2023/08/29に公開
1

React Hook Form の Controller コンポーネントを使用すると、カスタムフォームコンポーネントを簡単に統合できるため便利ですが、input type="file"との組み合わせで問題が発生する場合があります。

問題点

Controller を使用して input type="file"をラップした際に、フォームの送信時に返されるデータがファイルの実際のデータではなく、ファイルパスの文字列として返ってくる場合がある。

さらに、setValue でファイルの値を設定しようとすると、以下のようなエラーが発生する。

InvalidStateError: Failed to set the 'value' property on
'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

原因

input type="file"の value 属性はセキュリティ上の理由から、JavaScript から変更することが許可されてないので、React Hook Form の Controller が内部でファイル入力の値を制御しようとすると、上記のエラーが発生します。

解決策

ファイルのアップロードを適切に扱うには、以下の手順で可能になります。

"use client";
import { Controller, useForm } from "react-hook-form";

export default function Home() {
  const {
    handleSubmit,
    formState: { errors },
    control,
  } = useForm();

  const onSubmit = (data: any) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name="file"
        defaultValue=""
        render={({ field: { onChange } }) => (
            <>
              <label htmlFor={`checkbox-file`}>file</label>
              <input
                id={`checkbox-file`}
                type="file"
                onChange={(e) => onChange(e.target.files)}
              />
            </>
        )}
      />
      {errors.exampleRequired && <span>This field is required</span>}

      <input type="submit" />
    </form>
  );
}

Controller の render プロップを使用してカスタムレンダリングを行い、input type="file"の onChange イベントでファイルデータを取得します。
ファイルデータを state などに一時的に保存します。

必要に応じて、setValue を使用してファイルデータをフォームの状態にセットします。
ただし、input type="file"の value には直接セットしないよう注意してください。

この方法を使用することで、input type="file"の扱いで生じる問題を回避しながら、ファイルのアップロードを適切に処理することができます。

まとめ

React Hook Form の Controller を使用する際に input type="file"の問題に直面した場合、上記の方法で問題を回避できます。簡単な対処法を知っておくだけで、フォームの開発がよりスムーズに進められるでしょう。

Discussion