見出し画像

inertia.jsでreact-dropzoneを使ってみた結果

install

npm install react-dropzone

雛形

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import { Head, Link, router, useForm } from '@inertiajs/react'
import { useDropzone } from 'react-dropzone';
import { useState, useCallback } from 'react';

export default function ArtifactIndex({ auth }) {
  const [errorMessage, setErrorMessage] = useState('');
  const onDrop = useCallback((acceptedFiles, fileRejections) => {
    // Upload function here
  });
  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    maxFiles: 1,
  });

  return (
    <AuthenticatedLayout
      user={auth.user}
      header={
        <h2 className="font-semibold text-xl text-gray-800 leading-tight">
          Artifacts
        </h2>
      }
    >
      <Head title="Artifats" />

      <div className="py-6 px-4 mx-auto max-w-screen-xl">
        <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
          <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
            <div className="p-6 text-gray-900">

            </div>
          </div>
        </div>
      </div>
    </AuthenticatedLayout>
  )
}

maxFilesでファイルの数を制限している

デザイン

              {/* Dropzone area */}
              <div {...getRootProps({
                className: 'p-6 dropzone',
                'data-tooltip-id': errorMessage ? 'errorTooltip' : ''
              })}>
                <input {...getInputProps()} />
                <p className="border-2 border-dashed border-blue-500 rounded-md p-4 text-center text-blue-500 hover:border-blue-700">
                  ドラッグアンドドロップするかクリックしてファイルを選択してください
                </p>
              </div>

こんな感じで自分で作る。data-tooltip-id はエラーのときのツールチップ用だが、まあ、今はなんとなく置いてある

送信先とかをちゃんと書く

ここれはファイルをartifacts.storeに送信したい。とりあえずcontrollerではdumpを用意しておくだけ

    public function store(Request $request)
    {
        dd($request->all());
    }

アップロードするにはjsxのこの箇所だ

  const onDrop = useCallback((acceptedFiles, fileRejections) => {
    // Upload function here
  });

こうする

export default function ArtifactIndex({ auth }) {
  const [errorMessage, setErrorMessage] = useState('');
  const onDrop = useCallback((acceptedFiles, fileRejections) => {

    // FormData オブジェクトを作成
    const formData = new FormData();

    // 受け取ったファイルを FormData に追加
    acceptedFiles.forEach(file => {
      formData.append('files[]', file);
    });

    router.post(route("artifacts.store"), formData, {
      forceFormData: true,
    });

このようにアップロードできていることが理解できるが、ただ、2つファイルを放りこんだときは無反応になる

エラーハンドリング

export default function ArtifactIndex({ auth }) {
  const [errorMessage, setErrorMessage] = useState('');
  const onDrop = useCallback((acceptedFiles, fileRejections) => {

    setErrorMessage('');
    if (fileRejections.length > 0) {
      setTimeout(() => { setErrorMessage(''); }, 2000);
      return;
    } else {
      setErrorMessage('');
    }


    // FormData オブジェクトを作成
    const formData = new FormData();

    // 受け取ったファイルを FormData に追加
    acceptedFiles.forEach(file => {
      formData.append('files[]', file);
    });

    router.post(route("artifacts.store"), formData, {
      forceFormData: true,
    });

こんな感じでfileRejectionsにエラーの状態とかがあるので、それに応じてstateにいれている。あとはこれを表示すればいい

              {/* Dropzone area */}
              <div {...getRootProps({
                className: 'p-6 dropzone',
                'data-tooltip-id': errorMessage ? 'errorTooltip' : ''
              })}>
                <input {...getInputProps()} />
                <p className="border-2 border-dashed border-blue-500 rounded-md p-4 text-center text-blue-500 hover:border-blue-700">
                  ドラッグアンドドロップするかクリックしてファイルを選択してください
                </p>
                {/* エラーメッセージ表示 */}
                {errorMessage && (
                  <p className="text-red-500 text-center mt-2">
                    {errorMessage}
                  </p>
                )}
              </div>


tooltipを使ったりする


まあこれでもいいんだけど

とかやったのでこれを使ってみると

npm install react-tooltip
import { Tooltip } from 'react-tooltip';

でimportしといて

              {/* Dropzone area */}
              <div {...getRootProps({
                className: 'p-6 dropzone',
                'data-tooltip-id': errorMessage ? 'errorTooltip' : ''
              })}>
                <input {...getInputProps()} />
                <p className="border-2 border-dashed border-blue-500 rounded-md p-4 text-center text-blue-500 hover:border-blue-700">
                  ドラッグアンドドロップするかクリックしてファイルを選択してください
                </p>
              </div>
              {errorMessage && (
                <Tooltip
                  id="errorTooltip"
                  place="top"
                  variant="error"
                  content={errorMessage}
                  isOpen={true}
                />
              )}

みたいにする。でもこれだと出っぱなしになるから

    setErrorMessage('');
    if (fileRejections.length > 0) {
      setErrorMessage("一度にアップロードできるのは1つのファイルのみです。");
      setTimeout(() => { setErrorMessage(''); }, 2000);
      return;
    } else {
      setErrorMessage('');
    }

のようにTimeoutさせておいた。この辺はフロントエンドでぐにゅぐにゅすると楽なところであるな。

docxだけに絞るとかする


  const { getRootProps, getInputProps } = useDropzone({
    onDrop,
    maxFiles: 1,
    accept: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  });

でもこれ動かないんだって

    setErrorMessage('');
    // 拡張子に基づく追加の検証
    if (!acceptedFiles.every(file => file.path.endsWith('.docx'))) {
      setErrorMessage('サポートされているファイル形式は.docxのみです。');
      setTimeout(() => { setErrorMessage(''); }, 3000);
      return;
    }
    if (fileRejections.length > 0) {
      // エラーの原因によって異なるメッセージを設定
      const { errors } = fileRejections[0];
      if (errors.some(e => e.code === 'too-many-files')) {
        setErrorMessage('一度にアップロードできるのは1つのファイルのみです。');
      } else if (errors.some(e => e.code === 'file-invalid-type')) {
        setErrorMessage('サポートされているファイル形式は.docxのみです。');
      }

      setTimeout(() => { setErrorMessage(''); }, 3000);
      return;
    }

まあこの辺のコードは大概AIに書かせているから詳しくみてないけど動いてはいるみたいよ

この記事が気に入ったらサポートをしてみませんか?