🕺
React Hook Formを使ってドラッグ & ドロップ可能な画像アップロード用フォームを実装する
要件
- 画像プレビュー
- 画像プレビューをクリックしてファイル選択ダイアログ表示
- 画像プレビューにファイルをドロップで選択できる
つまづいた箇所
- divをクリックした時に子要素であるinputのclickイベントを発火させたいが、useRefの設定とregisterの登録を同時に行おうとするとうまくいかない
- divに対してファイルをドラッグ & ドロップしたときにinputのchangeイベントを発火させたいがうまくいかない
実装
cssや細かい箇所は所々省略している
import { useState, useRef } from "react";
const acceptImageFiles = ["image/png", "image/jpeg", "image/gif", "image/jpg"];
const loadImage = (files, inputFile, setImage) => {
const file = files[0];
const reader = new FileReader();
reader.onload = (event) => {
setImage(event.target.result);
}
reader.readAsDataURL(file);
};
export default function ImageUploadForm({imageSrc, alt, register}) {
const [image, setImage] = useState(imageSrc);
const inputFile = useRef(null);
const {ref, onChange, ...aRegister} = register;
return (
<div
onDragOver={(event) => event.preventDefault()}
onDrop={(event) => {
if (acceptImageFiles.includes(event.dataTransfer.files[0].type)) {
inputFile.current.files = event.dataTransfer.files;
inputFile.current.dispatchEvent(new Event("change", { bubbles: true }));
}
event.preventDefault();
}}
onDragLeave={() => console.log("ドラッグリーブ")}
onClick={(event) => {
inputFile.current.click();
event.stopPropagation();
}}
>
<img src={image} />
<input
type="file"
accept={acceptImageFiles}
onChange={(event) => {
if (event.target.files.length > 0) {
onChange(event);
loadImage(event.target.files, inputFile, setImage)
}
}}
ref={(element) => {
ref(element);
inputFile.current = element;
}}
{...aRegister}
/>
</div>
);
}
詳細説明
"divをクリックした時に子要素であるinputのclickイベントを発火させたいが、useRefの設定とregisterの登録を同時に行おうとするとうまくいかない"の解決方法
useRefを使ってinputを参照するための変数を用意する
react hook formのregisterをスプレッド構文で展開し、refを取りだす
const inputFile = useRef(null);
const {ref, onChange, ...aRegister} = register;
input要素のrefに対して、デフォルトのrefの実行に加えinputFileにinput要素を設定する実装を行う。
スプレッド構文で展開した残りのaRegiterを登録する
<input
type="file"
ref={(element) => {
ref(element);
inputFile.current = element;
}}
{...aRegister}
/>
divのonClickイベントでinputFile.current.click()を実行する
<div
onClick={(event) => {
inputFile.current.click();
event.stopPropagation();
}}
>
"divに対してファイルをドラッグ & ドロップしたときにinputのchangeイベントを発火させたいがうまくいかない"の解決方法
divのonDropイベントでuseRefで取得したinput要素のdispatchEventメソッドを使ってchangeイベントを発火させる
<div
onDrop={(event) => {
if (acceptImageFiles.includes(event.dataTransfer.files[0].type)) {
inputFile.current.files = event.dataTransfer.files;
inputFile.current.dispatchEvent(new Event("change", { bubbles: true }));
}
event.preventDefault();
}}
>
あとがき
他によい方法があったらご教示ください
Discussion