reactjstypescriptreact-dropzone

Why the file doesn't upload?


This code defines a modal component (CategorieModal) in React for adding or editing a category. It uses react-dropzone for file uploads and includes inputs for the category title and cover image. When a file is dropped, the onDrop function is called, updating the state with the dropped file and calling the uploadFile function to upload it. However, there seems to be an issue with the request format, resulting in a "Request failed with status code 400" error.

error uploading file: AxiosError {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}

the upload function :

 export const uploadFile = async (formData: formData) => {
  const fd = new FormData();
  fd.append("coverImgUrl", formData.coverImgUrl)
  return await axiosClient.post("/storage/uploadFile");
};   
import { useDropzone } from "react-dropzone";
import { useCallback, useEffect, useState } from "react";
import { addCategorieApi, editCategorieApi, uploadFile } from "../../../services/categorie";
import { useMutation } from "@tanstack/react-query";
import { Categorie } from "../../../interfaces/categorie";

type ModalProps = {
  setIsModalOpen: (value: boolean) => void;
  mode: string;
  editedForm: Categorie;
  onFileChange: (files: File[]) => void;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  currentItems: any;
};

const CategorieModal = ({
  setIsModalOpen,
  mode,
  editedForm,
  onFileChange,
}: ModalProps) => {

const onDrop = useCallback(
  async (acceptedFiles: File[]) => {
    onFileChange(acceptedFiles);
    const formData = {
      title: "",
      coverImgUrl: acceptedFiles[0],
    };
    try {
      await uploadFile(formData);
      console.log("File uploaded successfully");
    } catch (error) {
      console.error("Error uploading file:", error);
    }
  },
  [onFileChange]
);

  const {
    acceptedFiles: imageFilesAcceptedFiles,
    getRootProps: ImagesGetRootProps,
    getInputProps: ImageGetInputProps,
  } = useDropzone({
    onDrop,
  });
  const coverImgUrl =
    imageFilesAcceptedFiles.length > 0 ? (
      <li>
        {imageFilesAcceptedFiles[0].name} - {imageFilesAcceptedFiles[0].size}
      </li>
    ) : null;

 const [formData, setFormData] = useState({
   id: editedForm?.id || "",
   title: editedForm?.title || "",
   coverImgUrl:
     editedForm?.coverImgUrl ||
     (imageFilesAcceptedFiles.length > 0
       ? imageFilesAcceptedFiles[0].name
       : ""),
 });

  const { mutate: createCategorie } = useMutation({
    mutationFn: addCategorieApi,
    onSuccess: () => {},
  });
  const addCategorie = async () => {
    createCategorie({
      title: formData.title,
      coverImgUrl: imageFilesAcceptedFiles[0],
    });
    console.log(formData);
    setIsModalOpen(false);
  };

  const { mutate: editCategorieMutate } = useMutation({
    mutationFn: editCategorieApi,
    onSuccess: () => {
      console.log("categorie modifier avec succès");
    },
  });

  const editCategorie = async () => {
    console.log(imageFilesAcceptedFiles[0]);
    editCategorieMutate({
      coverImgUrl: imageFilesAcceptedFiles[0],
      title: formData.title,
      id: formData.id,
    });

    console.log(editedForm);
    setIsModalOpen(false);
  };

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };
  useEffect(() => {
    if (editedForm && mode === "edit") {
      setFormData({
        id: editedForm.id,
        title: editedForm.title,
        coverImgUrl: editedForm.coverImgUrl,
      });
    }
  }, [editedForm]);
  return (
    <div
      className="w-screen h-screen fixed inset-0 bg-gray-950/30 flex justify-center items-center z-10"
      onClick={() => setIsModalOpen(false)}
    >
      <div
        className="flex min-h-screen sm:block sm:p-0"
        onClick={(e) => e.stopPropagation()}
      >
        <span
          className="hidden sm:inline-block sm:align-middle sm:h-screen"
          aria-hidden="true"
        >
          &#8203;
        </span>
        <div className="inline-block w-2/3 h-2/3 bg-white rounded-lg  overflow-hidden  sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
          <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 border-b">
            <div className="text-center">
              <h3
                className="text-lg font-medium text-gray-900"
                id="modal-title"
              >
                {mode === "add" ? "إضافة عنصر" : "تعديل عنصر"}
              </h3>
            </div>
          </div>
          <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 ">
            <div className="mt-4 flex items-center">
              <label
                htmlFor="title"
                className="block text-sm font-medium text-gray-700 ml-12"
              >
                العنوان
              </label>
              <input
                type="text"
                name="title"
                value={formData.title}
                id="title"
                className="mt-1 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md p-2"
                placeholder="ادخل العنوان..."
                onChange={handleChange}
              />
            </div>
            <div className="mt-4 flex " {...ImagesGetRootProps()}>
              <label
                htmlFor="imgCover"
                className="text-sm font-medium text-gray-700 ml-4 text-start"
              >
                صورة الواجهة
              </label>
              <div className="border-2 border-dashed border-gray-400 p-4 ">
                <input
                  {...ImageGetInputProps({
                    name: "coverImgUrl",
                    accept: "image/*",
                  })}
                  type="text"
                  name="title"
                  id="title"
                  className="mt-1 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md p-2"
                  placeholder="ادخل العنوان..."
                />
                <p>سحب وإفلات بعض الملفات هنا، أو انقر لتحديد الملفات</p>
                <ul>
                  {mode === "add" ? (
                    coverImgUrl
                  ) : coverImgUrl ? (
                    coverImgUrl
                  ) : (
                    <li>{formData.coverImgUrl}</li>
                  )}
                </ul>
              </div>
            </div>
          </div>
          <div className="bg-white px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse border-t">
            <button
              className="inline-flex justify-center rounded-md border px-4 py-2 bg-primary font-medium text-white hover:bg-opacity-75 sm:ml-3 sm:w-auto sm:text-sm"
              onClick={mode === "add" ? addCategorie : editCategorie}
            >
              حفظ
            </button>
            <button
              onClick={() => {
                setIsModalOpen(false);
              }}
              type="button"
              className="mt-3 inline-flex justify-center rounded-md border border-gray-300 px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50  sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
            >
              الغاء
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default CategorieModal;


Solution

  • Looks like you might not be passing the form data to your api,

    Should update your request to this:

    export const uploadFile = async (formData: formData) => {
      const fd = new FormData();
      fd.append("coverImgUrl", formData.coverImgUrl)
      return await axiosClient.post("/storage/uploadFile",fd, {
         headers: {
            "Content-Type": "multipart/form-data"
         }
      });
    };