[React] 画像ファイルをドラッグ&ドロップで取得してみる | 心を無にして始める React

準備

react-dropzone を使っていきます。

https://react-dropzone.js.org/

react-dropzone のインストール

npm install react-dropzone

ドラッグ&ドロップでファイルを表示する

今回は複数ファイル(3つまで)のファイルをD&Dで受け取って、表示できるようにしてみます。

まずは D&D でファイルを受け取れるコンポーネントを作ります。

場所はこれまでと同じように components 配下にします。
名前は Dropzone で作っていきます。

import { useEffect } from 'react';
import { useMemo } from 'react';
import { useDropzone } from 'react-dropzone';

const baseStyle = {
  cursor: 'auto',
  flex: 1,
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  borderWidth: 2,
  borderRadius: 2,
  borderColor: '#eeeeee',
  borderStyle: 'dashed',
  backgroundColor: '#fafafa',
  color: '#bdbdbd',
  outline: 'none',
  transition: 'border .24s ease-in-out'
};

const focusedStyle = {
  borderColor: '#2196f3'
};

const acceptStyle = {
  borderColor: '#00e676'
};

const rejectStyle = {
  borderColor: '#ff1744'
};

const DropZone = (props) => {
  const {
    onDrop,
    children,
    ...other
  } = props;

  const {
    acceptedFiles,
    getRootProps,
    getInputProps,
    isFocused,
    isDragAccept,
    isDragReject
  } = useDropzone({ accept: 'image/*', noClick: true, maxFiles: 3 });

  useEffect(() => {
    if (acceptedFiles.length) {
      onDrop && onDrop(acceptedFiles);
    }
  }, [acceptedFiles]);

  const style = useMemo(() => ({
    ...baseStyle,
    ...(isFocused ? focusedStyle : {}),
    ...(isDragAccept ? acceptStyle : {}),
    ...(isDragReject ? rejectStyle : {})
  }), [
    isFocused,
    isDragAccept,
    isDragReject
  ]);

  return (
    <div className="container" {...other}>
      <div {...getRootProps({ style })}>
        <input {...getInputProps()} />
        {children}
      </div>
    </div>
  );
}

export default DropZone;

次に App.js を変更して、表示してみます。

import React, { useState } from 'react';
import './App.css';
import Dropzone from "./components/DropZone";

function App() {
  const [urls, setUrls] = useState([]);

  const onDrop = (acceptedFiles) => {
    console.log(acceptedFiles)
    if (acceptedFiles) {
      setUrls(acceptedFiles.map(acceptedFile => URL.createObjectURL(acceptedFile)))
    }
  }

  return (
    <>
      <div className="bg-dark p-5" style={{ minHeight: '100vh' }}>
        <Dropzone className="w-100" onDrop={onDrop}>
          <div className="d-flex justify-content-center align-items-center p-5">
            {
              urls.length === 0 && (
                <span>ファイルをドロップしてください</span>
              )
            }
            {
              urls.map(url => (
                <div key={url?.toString()} className="border border-dark m-2">
                  <img src={url} alt="" style={{ maxWidth: 320, maxHeight: 240 }} />
                </div>
              ))
            }
          </div>
        </Dropzone>
      </div>
    </>
  );
}

export default App;

動かしてみます。

はい、できました。