zukucode
主にWEB関連の情報を技術メモとして発信しています。

React ファイル選択のコンポーネントを作成する

React+TypeScriptの環境で、ファイル選択のコンポーネントを作成します。

完成品

最低限の機能ですが、以下のように実装します。

import { useCallback, useRef, useState } from 'react';

const App = () => {
  const [files, setFiles] = useState<File[]>([]); // 選択したファイルを配列で格納
  const attachRef = useRef<HTMLInputElement>(null); // ファイル選択のコントロールを参照

  // ファイル選択イベント
  const handleInpuFileChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files == null) return; // ファイルがない場合は処理終了
    const files = Array.from(e.target.files); // FileListを配列に変換
    setFiles((current) => current.concat(files)); // 選択したファイルを変数に追加
    if (attachRef.current) attachRef.current.value = ''; // 内部的なファイルの選択状態をクリア
  }, []);

  return (
    <>
      <div>
        {files.map((f) => (
          <div key={f.name}>{f.name}</div>
        ))}
      </div>

      <div>
        <input type="button" value="参照" onClick={() => attachRef.current?.click()}></input>
        <input type="file" style={{ display: 'none' }} ref={attachRef} multiple onChange={handleInpuFileChange}></input>
      </div>
    </>
  );
};

export default App;

解説

複数のファイルが選択できるように、選択したファイルは配列でstateで保持しています。

const [files, setFiles] = useState<File[]>([]); // 選択したファイルを配列で格納

選択したファイルのファイル名を1件ずつループで画面に出力しています。

ここではkeyをname(ファイル名)としているため、同名のファイルを複数選択するとエラーになります。

実際はユニークなIDなどもファイルとセットで一緒にstateで保持する形になると思います。

<div>
  {files.map((f) => (
    <div key={f.name}>{f.name}</div>
  ))}
</div>

ファイル選択のコントロール(input type="file")をそのまま使用してもよいですが、ブラウザによってスタイルが異なるので、今回は非表示としています。

参照ボタンクリックで、内部的にファイル選択のボタンをクリックしています。

ファイル選択のボタンを参照するため、attachRefという変数でファイル選択の要素を参照できるようにしています。

<div>
  <input type="button" value="参照" onClick={() => attachRef.current?.click()}></input>
  <input type="file" style={{ display: 'none' }} ref={attachRef} multiple onChange={handleInpuFileChange}></input>
</div>

ファイル選択イベントにて、e.target.filesで選択したファイルを取得できます。

FileListの型で取得されますので、扱いやすいようにArray.Fromで配列(File[])に変換しています。

ファイルの配列をstateに追加し、最後にファイルコントロールの選択状態をクリアします。

クリアしないと、同じファイルを再度選択したときに、onChangeイベントが発生しなくなってしまいます。

// ファイル選択イベント
const handleInpuFileChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
  if (e.target.files == null) return; // ファイルがない場合は処理終了
  const files = Array.from(e.target.files); // FileListを配列に変換
  setFiles((current) => current.concat(files)); // 選択したファイルを変数に追加
  if (attachRef.current) attachRef.current.value = ''; // 内部的なファイルの選択状態をクリア
}, []);

このコンポーネントに対して、React ドラッグ&ドロップでファイルを選択するでドラッグ&ドロップでファイルを追加する方法を紹介しています。


関連記事