【ドラッグ&ドロップでファイルアップロード】react-dropzoneの使い方

2024/05/21に公開

はじめに

inputタグのtype属性を'file'に設定することでファイルのアップロードが可能ですが、UIをよりわかりやすくするために、以下のようにドラッグ&ドロップ機能を用いてファイルをアップロードできるようにします。

react-dropzone

react-dropzoneは、Reactのコンポーネントライブラリでありドラッグ&ドロップインターフェースを簡単に実装できるライブラリです。
https://react-dropzone.js.org/

インストールしてください。

npm install react-dropzone

ファイルアップロード用のコンポーネント作成

componentsフォルダにファイルアップロード用のコンポーネントを作成します。
今回は、DropzoneComponent.tsxという名前でファイルを作成します。

mkdir -p src/app/components && touch src/app/components/DropzoneComponent.tsx

下記の内容を記述してください。

src/app/components/DropzoneComponent.tsx
import { CSSProperties, useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';

const styles: CSSProperties = {
  border: '2px dashed #cccccc',
  borderRadius: '5px',
  padding: '20px',
  textAlign: 'center',
  cursor: 'pointer',
};

const DropzoneComponent = () => {
  const [file, setFile] = useState<File | null>(null);

  const onDrop = useCallback((acceptedFiles: File[]) => {
    setFile(acceptedFiles[0]);
    console.log(acceptedFiles);
  }, []);

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

  return (
    <div {...getRootProps()} style={styles}>
      <input {...getInputProps()} />
      {isDragActive && <p>ここにファイルをドロップしてください。</p>}

      {!file && !isDragActive && (
        <p>
          ファイルをドラッグ&ドロップするか、ここをクリックしてファイルを選択してください。
        </p>
      )}

      {file && (
        <div>
          <p>アップロードされたファイル名 </p>
          <p>{file.name} </p>
        </div>
      )}
    </div>
  );
};

export default DropzoneComponent;

ページコンポーネントでの使用

<DropzoneComponent />を追記してください。

isDragActive

isDragActiveは、ドラッグ中のアイテムがドロップゾーンの範囲内にある場合にtrueになります。

Types of property 'textAlign' are incompatible.

https://github.com/typestyle/typestyle/issues/281

下記の場合、style={styles}

const styles = {
  border: '2px dashed #cccccc',
  borderRadius: '5px',
  padding: '20px',
  textAlign: 'center',
  cursor: 'pointer',
};

次のようなエラーが出ます。

これは、stylesオブジェクトの各プロパティの型は自動的に推論され、特定の文字列リテラル型ではなく、string型として扱われることが多いです。これが原因で、Reactのstyleプロパティが期待する特定の文字列リテラル型と一致せず、エラーが発生することがあります。

Type '{ border: string; borderRadius: string; padding: string; textAlign: string; cursor: string; }' is not assignable to type 'Properties<string | number, string & {}>'.
  Types of property 'textAlign' are incompatible.
    Type 'string' is not assignable to type 'TextAlign | undefined'.ts(2322)
index.d.ts(2908, 9): The expected type comes from property 'style' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'

解決するには、textAlign: 'center'textAlign: 'center' as constに変更、
またはCSSProperties型を明示的に指定して、textAlignの値として'center'が適切であることを保証してください。

const styles: CSSProperties = {
  border: '2px dashed #cccccc',
  borderRadius: '5px',
  padding: '20px',
  textAlign: 'center',
  cursor: 'pointer',
};

as const

as constは、TypeScriptにリテラル型を使うように指示します。
リテラル型は、値が特定の値であることを示す型です。つまり、'center' as constとすることで、その値が厳密に'center'であると型付けされます。
https://zenn.dev/nenenemo/articles/dd4417b5c99080#as-const:constアサーション

終わりに

何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉

Discussion