Search code examples
reactjsasynchronousasync-awaitfetchtop-level-await

React - Frontend Component submit handler calling API function doing a fetch, but not waiting for the returned value


I have a handler for a form submit that takes a user image, sends it to the back, does stuff on that image, and gives the modified image back (as a base64) to the front to be displayed.

Sending and receiving the image from the back works fine. But after receiving the image in the front API, I cannot give it to front Component, because the component handler is not waiting for the front API return.

Component that has the form and handler :

function Workshop() {

  // Used to store and update the front when data are modified
  const [imageUpload, setImageUpload] = useState(); 
  const [imageGenerated, setImageGenerated] = useState();

  // Handle the submit of the form
  const handleSubmit = event => {
    event.preventDefault();

    // Append the user image to the formData object
    setImageUpload(event.target.files[0]);
    const formData = new FormData();
    formData.append("file", imageUpload);

    // Calls the FrontEnd API with the image to be modified
    if(imageUpload) {
      const res = frontAPI.callGenerateImage(formData);
      console.log("back to front"); // ### THIS IS WHERE IT DOESNT WORK ###
      console.log(res); // ### THIS IS WHERE IT DOESNT WORK ###
      setImageGenerated(res); // ### THIS IS WHERE IT DOESNT WORK ###
    }
  };

  return (                                                                                                                                                                                                                                                                                                                                        
    <>
      <div className="workshop">
        <form method="post" onSubmit={handleSubmit} encType="multipart/form-data">
            <input type="file" name="imageUploader" onChange={handleImagePreview} />
            <img alt="preview image" src={imagePreview} />
            <button type="submit">Modify Image</button>
        </form>
        <img alt="generated image" src={"data:image/jpeg;base64," + imageGenerated} />
      </div>
    </>
  );
}

export default Workshop;

FrontEnd API with the fetch :

export function callGenerateImage(formData) {
  var  base64;
  fetch("/generator", {
    method: "POST",
    body: formData
  })
  .then(data => data.json())
  .then(res => {
    base64 = Buffer.from(res.data, "binary" ).toString("base64");
    console.log(base64); // THIS IS THE BASE64 STRING IN THE OUPUT BELOW
    return base64;
  })
};

Output :

enter image description here

So as you can see, I do receive the base64 stream of my modified image, but the line const res = fontAPI.callGenerateImage(formData); doesn't wait for the actual return of the fetch and goes on with the console.log("back to front"); .

I tried to switch my API function to an async function and change

const res = frontAPI.callGenerateImage(formData);

to

const res = await frontAPI.callGenerateImage(formData);

But it doesn't work because I'm not at top level, even if I remove the "if", as I am in a function of the component.

I'm fairly sure that the answer is pretty simple but I don't see it.

Thanks a lot for your help !

EDIT : PROBLEM SOLVED, SEE MY ANSWER BELOW


Solution

  • I got it to work thanks to jbcortez89 and lo-fi wi-fi.

    I needed to fix 2 things in my code :

    A) Switching the handler to an async function, which I didn't even know was possible this way, which somehow allows me to use await even if I'm not at top level :

    const handleSubmit = async (event) => {
    event.preventDefault()
    if (imageUpload) {
        const formData = new FormData();
        formData.append("file", imageUpload);
    
        const res = await frontAPI.callGenerateImage(formData);
        setImageGenerated(res);
    }
    

    B) Fix my API fetch return value. I decided to switch to the new format which is much clearer than the nested then().

    export async function callGenerateImage(formData) {
      let myData = await fetch("/generator", {
        method: "POST",
        body: formData
      })
      
      let dataJSON = await myData.json();
      return Buffer.from(dataJSON.data, "binary" ).toString("base64"); 
    };
    

    thanks a lot guys for your help !