Search code examples
javascriptreactjses6-promisefetch-api

Chaining promises including fetch


I have a React form with a file as an input, and the onFileChange saves setFile(e.target.files[0]) (and also toggle a boolean change). Then when I submit the form:

  • I first want to upload this file in the cloud (Cloudinary here),
  • wait for the response object (namely the url and public_id I'm looking for)
  • then I add this object (url and 'public_id) to the formdata to post to the database backend.

I thought that chaining promises should do the job, but I can't achieve it.

Inside my onFormSubmit, I first define a promise that captures the none async data:

function init(fd){
  fd.append('input1'...)
  return Promise.resolve(fd)
}

so I can reuse the formdata to feed the next promise upLoadToCL that should 'normally' asynchronously append the response object from the cloud to the formdata, with:

init(new FormData).then(res => upLoadToCL(res)).then(res=> ...)

function upLoadToCL(fd) {
      if (changed) {
        // send 'file' (saved as state variable after input) to the cloud
        const newfd = new FormData();
        newfd.append("file", file);
        newfd.append("upload_preset", "ml_default");
        fetch(`https://api.cloudinary.com/v1_1/${cloudName}/upload`, {
          method: "POST",
          body: newfd,
        })
          .then((res) => res.json())
          // append the formdata argument 'fd' with the result
          .then((res) => {
            setPhoto(res);
            fd.append("event[directCLUrl]", res.url);
            fd.append("event[publicID]", res.public_id);
          })
          .catch((err) => {
            throw new Error(err);
          });
        return Promise.resolve(fd);
      }
    }

I checked that the first promise works and sends to the second promise a 'prefilled' formdata. Then the post request works, and returns a response as I can see that the state variable photo is updated some time in the futur. However, the promise itself returns a void formdata, even without chaining:

upLoadToCL(new FormData())
  .then(res=> {
    for (let [k,v] of res){
      console.log(k,v)
    }
  })

returns nothing.


Solution

  • You're already doing the promise chaining fine. You just need to return the result of that promise chain instead of the Promise.resolve(fd) from your function:

    function upLoadToCL(fd) {
      if (changed) {
        // send 'file' (saved as state variable after input) to the cloud
        const newfd = new FormData();
        newfd.append("file", file);
        newfd.append("upload_preset", "ml_default");
        return fetch(`https://api.cloudinary.com/v1_1/${cloudName}/upload`, {
    //  ^^^^^^
          method: "POST",
          body: newfd,
        })
        .then((res) => res.json())
        .then((res) => {
          setPhoto(res);
          // append the formdata argument 'fd' with the result
          fd.append("event[directCLUrl]", res.url);
          fd.append("event[publicID]", res.public_id);
          return fd;
    //    ^^^^^^ fulfilling the promise with the updated fd
        });
      } else {
        return Promise.resolve(fd);
    //  ^^^^^^ always returning a promise for fd from uploadToCL()
      }
    }