Search code examples
reactjscanvasblobform-data

React Canvas converted to Blob and appended to Formdata initially gives and error and then starts working


My react app is for ticket sale that takes an existing image and stamps the guest name on it (using canvas). I have this form that sends a formData to the server

 <Formik
                validationSchema={schema}
                onSubmit={async (values) => {
                  try {
                    // Create a canvas element, add the image and text, covert to blob
                    //for 1500 x 485 images
                    var canvas = document.createElement("canvas");
                    var layout = canvas.getContext("2d");
                    let ticket = new Image();
                    ticket.src = target.ticket_img;
                    //image
                    canvas.width = ticket.naturalWidth;
                    canvas.height = ticket.naturalHeight;
                    layout.drawImage(
                      ticket,
                      0,
                      0,
                      ticket.naturalWidth,
                      ticket.naturalHeight
                    );
                    // text
                    let textName = values.name;
                    layout.rotate(4.71);
                    layout.font = "52px Archive";
                    layout.fillStyle = "#faf9f6";
                    layout.textAlign = "center";
                    layout.strokeText(textName, -255, 1170);
                    layout.fillText(textName, -255, 1170);

                    layout.font = "52px Archive";
                    layout.fillStyle = "#faf9f6";
                    let textSurname = values.surname;
                    layout.textAlign = "center";
                    layout.strokeText(textSurname, -255, 1230);
                    layout.fillText(textSurname, -255, 1230);
                    // blob
                    const dataBlob = await new Promise((resolve) =>
                      canvas.toBlob((blob) => resolve(blob), "image/webp")
                    );
                    // formData
                    const formData = new FormData();
                    formData.append(
                      "image",
                      dataBlob,
                      target.title +
                      "_" +
                      values.name +
                      values.surname +
                      "_GUEST"
                    );
                    // some more code that is not causing any errors
                  } catch (err) { 
                    console.log(err)
                   }

the problem is that when I first submit, I get the following error that comes from the catch block with console.log

TypeError: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'. at onSubmit (NonMemberPurchase.jsx:135:1)

and on the second submit (pressing the button once again), the form is submitted without problems and without errors. I suspect that I am scheduling the events poorly but I do not know how to fix it. I also do not want to change the canvas to blob converting functionality as this once works and the others do not output the ticket canvas properly. I am currently using the code as it is but people have started noticing the bug and start complaining that they need to press twice for the form to be submitted. I would appreciate some suggestions!


Solution

  • Okay so after some sessions I was finally able to figure it out! I shifted my convert canvas to Blob function out of the scope

    export const createCustomerTicket = async (ticketImage, name, surname) => {
        // Create a canvas element, add the image and text, covert to blob
        //for 1500 x 485 images
        var canvas = document.createElement("canvas");
        var layout = canvas.getContext("2d");
        let ticket = new Image();
        ticket.src = ticketImage;
    
        await new Promise((resolve, reject) => {
          ticket.onload = resolve;
          ticket.onerror = reject;
        });
    
        //image
        canvas.width = ticket.naturalWidth;
        canvas.height = ticket.naturalHeight;
        layout.drawImage(
          ticket,
          0,
          0,
          ticket.naturalWidth,
          ticket.naturalHeight
        );
        // text
        layout.rotate(4.71);
        layout.font = "52px Archive";
        layout.fillStyle = "#faf9f6";
        layout.textAlign = "center";
        layout.strokeText(name, -255, 1170);
        layout.fillText(name, -255, 1170);
    
        layout.font = "52px Archive";
        layout.fillStyle = "#faf9f6";
        layout.textAlign = "center";
        layout.strokeText(surname, -255, 1230);
        layout.fillText(surname, -255, 1230);
        
        // blob
        const dataBlob = await new Promise((resolve) =>
          canvas.toBlob((blob) => resolve(blob), "image/webp")
        );
    
        return dataBlob
      }
    

    Then I just call it async in the submit handler

     <Formik
                      validationSchema={schema}
                      onSubmit={async (values) => {
                        try {
                          const ticket = await createCustomerTicket(target.ticket_img, values.name, values.surname);
                          // formData
                          const formData = new FormData();
                          formData.append(
                            "image",
                            ticket,
                            target.title +
                            "_" +
                            values.name +
                            values.surname +
                            "_GUEST"
                          );
    //other code
    

    It works like a charm and I do not get it as a null value anymore!