📄

TypeScriptでreact-dropzoneを使ってドロップされたファイル名の表示

2022/09/03に公開

本記事は以下の記事のコードを参考にさせていただきました。
JavaScriptでreact-dropzoneの使い方を解説されています。
https://qiita.com/FumioNonaka/items/4ae1ccbfe609e1a10c4d

前提

  • typescriptでreact-dropzoneを使用したい
    • npx create-react-app my-app --template typescriptでプロジェクトを作り、npm install react-dropzoneした
    • "react": "^18.2.0",
    • "react-dropzone": "^14.2.2",
  • ドロップされたファイルの名前を表示したいが、react-dropzoneから返されるacceptedFilesに.pathがないというエラーが出る
import { useCallback, useMemo } from 'react';
import { useDropzone } from 'react-dropzone';

// ドロップされたときに実行する関数
const onDrop = useCallback((files: File[]) => {
    // Do something with the files
    console.log('files:', files);
  }, []);

const { getRootProps, getInputProps, isDragActive, acceptedFiles } = useDropzone({ onDrop });

// ドロップされたファイルパスを表示するli要素を準備
// ここで.pathがないエラー
const files = useMemo(() =>
    acceptedFiles.map(file => (
      <li key={file.path}>
        {file.path} - {file.size} bytes
      </li>
    )
    ), [acceptedFiles]);

解決方法

useDropzone()で返されるacceptedFilesの型がFile[]になっているのが原因。react-dropzoneにFileWithPathという型があるのでそれを使う。

import { useCallback, useMemo } from 'react';
import { useDropzone, FileWithPath } from 'react-dropzone';

const onDrop = useCallback((files: File[]) => {
    // Do something with the files
    console.log('files:', files);
  }, []);
const { getRootProps, getInputProps, isDragActive, acceptedFiles } = useDropzone({ onDrop });
// 型を変更。もっと良い方法があるはず
const filesUpdated: FileWithPath[] = acceptedFiles;

const files = useMemo(() =>
  filesUpdated.map(file => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  )
  ), [filesUpdated]);

コード全体

App.tsx
import { useCallback, useMemo } from 'react';
import { useDropzone, FileWithPath } from 'react-dropzone';

const styleDiv = {
  margin: "1%"
}

const style = {
  width: 200,
  height: 150,
  border: "1px dotted #888"
};


function App() {
  const onDrop = useCallback((files: File[]) => {
    // Do something with the files
    console.log('files:', files);
  }, []);
  const { getRootProps, getInputProps, isDragActive, acceptedFiles } = useDropzone({ onDrop });
  const filesUpdated: FileWithPath[] = acceptedFiles;

  const files = useMemo(() =>
    filesUpdated.map(file => (
      <li key={file.path}>
        {file.path} - {file.size} bytes
      </li>
    )
    ), [filesUpdated]);

  return (
    <div style={styleDiv}>
      <div {...getRootProps()} style={style}>
        <input {...getInputProps()} />
        {
          isDragActive ?
            <p>Drop the files here ...</p> :
            <p>Drag 'n' drop some files here, or click to select files</p>
        }
      </div>
      <aside>
        <h4>Files</h4>
        <ul>{files}</ul>
      </aside>
    </div>
  );
}

export default App;

このように表示される。test.jpgをドロップした状態。

参考

https://stackoverflow.com/questions/55728316/file-object-does-not-have-a-path-property-typescript

  • 同様の問題。FileWithPathreact-dropzoneからimportして解決

https://github.com/react-dropzone/react-dropzone/issues/981

  • react-dropzoneのissue。同様の問題についてacceptedFilesFile型になっているのはおかしいのではという指摘。FileWithPathreact-dropzoneからimportして解決

ここでは↓のように変換している

const fileList = (files: FileWithPath[]): ReactNode => (
  files.map(file => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ))
);

Discussion