Search code examples
reactjsmongodbformsnext.jscloudinary

Better option for image upload (cloud) in NextJS


I'm currently developing my first real project for a client with NextJS and MongoDB and I'm having problems uploading images. I'm working with Cloudinary but it can't receive multiple files and I'm also having issues with state management because when the form is submitted my database doesn't receive the files whereas Cloudinary does.

The API works fine so I post here the code of the form (REACT).

  export default function NewProduct() {
  const initialState = {
    title: "",
    price: 0,
    description: "",
    content: "",
    images: [],
    category: "tortas",
  };

  const [product, setProduct] = useState(initialState);
  const { title, price, description, content, category } = product;

  const [files, setFile] = useState("");


  //const handleChangeInput = (e) => {
  //  setProduct({ ...product, [e.target.name]: e.target.value });
  //};

  const handleUploadInput = async (e) => {
    const uploadFiles = [...e.target.files];
    setFile([...files, uploadFiles]);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    for (let file of files) {
      formData.append("file", file);
    }

    formData.append("upload_preset", "balbla");

    const res = await fetch(
      "https://api.cloudinary.com/v1_1/blabla/image/upload",
      {
        method: "POST",
        body: formData,
      }
    );
    const data = await res.json();
    setProduct((p) => ({ ...p, images: data.secure_url}));
    await createProduct();
    setProduct(initialState);
  };

  const createProduct = async () => {
    try {
      const res = await fetch("http://localhost:3000/api/products", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(product),
      });
      const data = await res.json();
      console.log(data);
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <Layout>
      <div className={styles.formDiv}>
        <form className={styles.form} onSubmit={handleSubmit}>
          
          
          <input
            type="file"
            name="file"
            onChange={handleUploadInput}
            multiple
            accept="image/*"
          />
          
          
          <button type="submit">Crear</button>
        </form>
      </div>
    </Layout>
  );
}

In case using Cloudinary isn't the best option with NextJS, what other cloud or stuff I could use? I hope I made myself clear. Thank you in advance.


Solution

  • The Cloudinary Upload API unfortunately doesn't support uploading multiple resources within a single request, so you would need to likely loop through all of your media items and upload them each individually.

    As far as the production creation is concerned, I can't tell for sure, but you may be trying to create the product before your product state is updated.

    Inside createProduct have you checked to see if all of the data you expect is available at the time it's being ran?

    You could try listening to updates to the product state and create a product based off of that with a useEffect hook, for instnace:

    useEffect(() => {
      // Check if product is available or perform
      // a check that you know isn't ready to create yet
      if ( !product ) return;
      (async function run() {
        await createProduct(product);
        setProduct(initialState);
      })()
    }, [product])