2

I'm trying to use cloudinary to host all the image data for when I make a file upload but I keep getting AjaxUploader Uncaught (in promise) TypeError: request is not a function error. It's hard for me to figure out how to use this with ant designs upload component which I got from the offical docs. It should be able to upload up to 5 files and if there are more, it conditionally un-renders the upload button, which I have already done. I just dont know how to handle the customRequest attribute, all my work is being done on the serverUpload function. Here is the link to sandbox: https://codesandbox.io/s/happy-sun-n9uus?file=/src/App.js

import React, { useState } from "react";
import Modal from "react-modal";
import { Form, Input, Button, Upload, message } from "antd";
import { UploadOutlined } from "@ant-design/icons";
import axios from "axios";
import cloudinaryInfo from "./cloudinaryInfo/config.js";

const layout = {
  labelCol: {
    span: 8
  },
  wrapperCol: {
    span: 16
  }
};
/* eslint-disable no-template-curly-in-string */

const validateMessages = {
  required: "${label} is required!",
  types: {
    email: "Your email is not a valid email!"
  }
};
/* eslint-enable no-template-curly-in-string */

const Question = ({ questionObj, productObj, updatedDataList }) => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [imageFilesList, setImageFilesList] = useState([]);

  const toggleModal = (e) => {
    e.preventDefault();
    e.stopPropagation();
    setIsModalOpen(!isModalOpen);
  };

  const serverUpload = async (options) => {
    const { onSuccess, file, onError, onProgress } = options;
    console.log("imageFilesList: ", imageFilesList);
    try {
      const result = await Promise.all([]);
      for (let i = 0; i < imageFilesList.length; i++) {
        let file = imageFilesList[i];
        console.log("FILE: ", file);
        const formData = new FormData();
        formData.append("file", file);
        formData.append(
          "upload_preset",
          cloudinaryInfo.CLOUDINARY_UPLOAD_PRESET
        );
        result.push(
          axios.post(cloudinaryInfo.CLOUDINARY_IMAGE_UPLOAD_URL, formData)
        );
      }
      onSuccess("ok");
    } catch (err) {
      console.log(err);
      onError(err);
    }
  };

  const uploadProps = {
    name: "file",
    customRequest: { serverUpload },
    onChange(info) {
      if (info.file.status !== "uploading") {
        console.log("Not uploading ", info.file, info.fileList);
      }
      if (info.file.status === "done") {
        message.success(`${info.file.name} file uploaded successfully`);
      } else if (info.file.status === "error") {
        message.error(`${info.file.name} file upload failed.`);
      }
      setImageFilesList(info.fileList);
    },
    listType: "picture",
    maxCount: 5,
    multiple: true,
    onDrop: true
  };

  const modalContent = (
    <Form
      {...layout}
      name="nest-messages"
      onFinish={(values) => console.log(values)}
      validateMessages={validateMessages}
    >
      <Upload {...uploadProps}>
        {imageFilesList.length < 5 && (
          <Button icon={<UploadOutlined />}>Upload (Max: 5)</Button>
        )}
      </Upload>
      <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 8 }}>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form.Item>
    </Form>
  );

  return (
    <div className="question">
      <button onClick={(e) => toggleModal(e)}>Open Modal</button>
      <hr style={{ height: 0.5, borderColor: "red" }} />
      {isModalOpen && (
        <div className="openPanel">
          <Modal
            isOpen={isModalOpen}
            onRequestClose={(e) => toggleModal(e)}
            ariaHideApp={false}
            style={{
              overlay: {
                backgroundColor: "grey"
              }
            }}
          >
            {modalContent}
            <Button onClick={(e) => toggleModal(e)}>Close</Button>
          </Modal>
        </div>
      )}
    </div>
  );
};

export default Question;
  • In `axios.post` call are you using a valid url? or 'mylink'... What does the error say in the catch block of your axios call – JohnSnow Sep 05 '21 at 04:54
  • yea it's a valid link, i just didnt want to share it to the public. I get, VM3050:1 POST https://api.cloudinary.com/v1_1/mycloudname/image/upload 400 (Bad Request) – Ronny Fitzgerald Sep 05 '21 at 05:02
  • @RonnyFitzgerald, normally you should get more information on why this is a bad request. Do you mind opening a ticket to support@cloudinary.com with your cloud_name at least we can investigate further? – Loic VdB Sep 06 '21 at 13:52
  • @LoicVdB I created a react-app playground to test cloudinary without ant design for just a single file upload and it works so the problem isn't on cloudinarys side I think, It's how I'm combining it with ant designs upload component that creates problems – Ronny Fitzgerald Sep 06 '21 at 19:53

1 Answers1

0

serverUpload() is a custom function to handle the request. You don't pass it to the action prop, you have to pass it to the antd upload customRequest prop, that's where custom requests go.

The action prop is for URLs that accept files, once you're making any modification to antd's upload default action you have to use customRequest.

See here

deltanboi
  • 65
  • 2
  • 7
  • okay, so I changed the attribute to custom request and passed in the object { onSuccess, onError, file, onProgress } into my serverUpload function. Do I just need OnSuccess and onError to be invoked inside my then and catch blocks respectively since I already have the files from my imageFilesList hook? – Ronny Fitzgerald Sep 06 '21 at 20:53
  • I'm getting a AjaxUploader.js:320 Uncaught (in promise) TypeError: request is not a function at AjaxUploader2.post after doing that – Ronny Fitzgerald Sep 06 '21 at 21:02
  • You're probably getting the error because you're using `forEach` to make your API calls and the promises aren't being caught. Checkout the solution suggested [here](https://stackoverflow.com/a/45611657/11302593). When all the calls have been successful you can call `onSucess("ok")` to let antd know the requests were successful. And you can pass the error to `onError` like so `onError({ err })`. You can checkout a demo [here](https://stackblitz.com/edit/so-58128062-upload-progress) – deltanboi Sep 06 '21 at 21:25
  • i used a for loop instead of a forEach I even tried doing a single file at a time and I still get the uncaught in promise error – Ronny Fitzgerald Sep 07 '21 at 06:03
  • @RonnyFitzgerald Can you share your code sample in like a sandbox or something, let me check it out. – deltanboi Sep 07 '21 at 08:50
  • https://codesandbox.io/s/happy-sun-n9uus?file=/src/App.js – Ronny Fitzgerald Sep 07 '21 at 20:23
  • I made some changes to the `serverUpload` function. You can test it out and let me know if it works. I get a `405` error cos the cloudinary details aren't set up (for secrutiy reasons obviously), so you can just copy the function into your own working environment and test it out and let me know. The link is [here](https://codesandbox.io/s/keen-fermi-hnl7p?file=/src/App.js) – deltanboi Sep 08 '21 at 11:36
  • I got the same error, AjaxUploader Uncaught (in promise) TypeError: request is not a function error. – Ronny Fitzgerald Sep 08 '21 at 20:40
  • Wow, well I'm beat. Sorry I couldn't help you out on this. I really hope you're able to find a solution. Let me know if you do find one. – deltanboi Sep 09 '21 at 12:29
  • thanks for trying, I kinda just gave up. I might use cloudinarys widget instead – Ronny Fitzgerald Sep 09 '21 at 22:30
  • Yh I was going to say you should try the widget. – deltanboi Sep 11 '21 at 00:09