1

After trying not to redirect the user that has submitted the form, I had trouble sending the form to my form service.

I setup a Formspark form to use on framer.com. Framer.com forces me to use Typescript which I don't know (I only know HTML CSS and JS right now). I gathered ideas and random information from Formspark's documentation and sitepoint's community. I would really (really) appreciate it if someone created some free time to help solve my problem.

Here's my form (simplified):

import React, { useEffect, useRef, useState } from "react"
import styled from "styled-components"

const SalesForm = styled.form`
  /*styles*/
`
const FormInput = styled.input`
  /*styles*/
`
const Button = styled.button`
  /*styles*/
`
const SuccessMessage = styled.div`
  /*styles*/
`
const ErrorMessage = styled.div`
  /*styles*/
`

export default function Form(props) {
    const captchaRef = useRef<HTMLDivElement>(null)
    const [token, setToken] = useState<string | null>(null)
    const [message, setMessage] = useState<string | null>(null)
    const [isSuccess, setIsSuccess] = useState<boolean | null>(null)

    useEffect(() => {
        console.log("Initializing hCaptcha...")
        if (window.hcaptcha) {
            window.hcaptcha.render(captchaRef.current!, {
                sitekey: "mycaptchacode",
                callback: (token: string) => {
                    console.log("hCaptcha token generated:", token)
                    setToken(token)
                },
            })
        }
    }, [])

    const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault()
        if (!token) {
            alert("Please verify that you are human.")
            return
        }

        const formData = new FormData(event.currentTarget)
        console.log("Form Data: ", Array.from(formData.entries()))

        try {
            const response = await fetch("https://submit-form.com/formid", {
                method: "POST",
                body: formData,
            })

            if (response.ok) {
                setMessage("Form successfully submitted!")
                setIsSuccess(true)
                event.currentTarget.reset()
            } else {
                const errorText = await response.text()
                console.error("Error response: ", errorText)
                setMessage(
                    "There was a problem with your submission: " + errorText
                )
                setIsSuccess(false)
            }
        } catch (error) {
            console.error("Submission error: ", error)
            setMessage(
                "There was an error submitting the form: " + error.message
            )
            setIsSuccess(false)
        }
    }

    return (
        <SalesForm id="sales-form" onSubmit={handleSubmit}>
            <script src="https://js.hcaptcha.com/1/api.js" async defer></script>
            <FormInput type="text" id="comp-name" name="comp-name" placeholder="comp-name" required/>
            <div ref={captchaRef}></div>
            <Button type="submit">Send</Button>
            {message &&
                (isSuccess ? (
                    <SuccessMessage>{message}</SuccessMessage>
                ) : (
                    <ErrorMessage>{message}</ErrorMessage>
                ))}
        </SalesForm>
    )
}

I'd like to give a bit more information:

  • handleSubmit was working when it didnt have anything else than the 1st "if" statement that forces the user to verify captcha (therefore, my form had method and action attributes), it didnt also have "async". I added those other lines to stay on the same page after submitting. I was just using a <input type="hidden" name="_redirect" value="redirecturl.com" />. Not staying on the same page was the main thing that dragged me all the way here. I don't want the user to be redirected.
  • Based on all of the above, I suspect that using captcha isn't the problem. And my form service was working until I tried not to redirect.
  • "Failed to fetch" is the error that displays below the form after I tried to send it.

I would really appreciate your help.

0

2 Answers 2

1

From what I can tell, it should work, the frontend logic seems okay. I think you need to check the url where you post. A few things to note

All you should need to prevent redirection is to call preventDefault on the submit event. So you have event.preventDefault() and that works fine.

As for the rest, I was not able to replicate your error, "Failed to fetch". I made a replit with your code on https://replit.com/@maxmezzomo/Form-Submit. I just removed the early return and alert for the captcha, I don't think that's the issue either.

You are however posting to https://submit-form.com/formid which returns a 404 with message "This form does not exist anymore". Even from the browser that's what you get if you click the link. So that's expected behaviour I would say. But it is inline with failed to fetch. I would just think you need to actually have an id if you want to post, so something like https://submit-form.com/<formid> where formid is where you want to post.

Once you have a url that points to a url expected by server, you should be able to make your request. On Formspark it seems the endpoint expects the following headers https://documentation.formspark.io/examples/ajax.html#axios-with-recaptcha-v2.

headers: {
  "Content-Type": "application/json",
  Accept: "application/json",
},

once you add those I think it should work, you probably want the payload to also be json so you can do something like

JSON.stringify(Object.fromEntries(Object.entries(formData)))

which should give json of an object with the form data, what you will need depends on what the api expects, but you can look into that.

I updated the replit with this, seems to work, get 200.

So now the response has code 200, so the form data was successfully submitted. But there's a part of the code that still throws an error which gets caught later, you can't use event currentTarget after handling the event, I don't know how it works exactly, but probably since its async by the time you handle the response of the request currentTarget is null. So the easy way around is to create a reference to the target before you make the request

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    const target = event.currentTarget;

    event.preventDefault()
    if (!token) {...

then you can use that variable to call reset.

Now UI also should show Form successfully submitted!

7
  • Hey, thank you for replying! I am using an actual "formid" there. Use submit-form.com/aDAJt6OP . If you dont use hcaptcha it will not send the form though. Commented May 29, 2024 at 18:02
  • 1
    yea np, okay cool, thanks, with the actual id it returns 302. So I see the error you mentioned. Looking at the formspark docs though it would like the data in json and with appropriate json headers, I'll update answer with that, once I add the headers I get 200. For the captcha, at least on replit, I just commented out those lines so it's submitting
    – Max
    Commented May 29, 2024 at 18:15
  • hey man, thank you so much for helping me this far. You can clear any lines that are about captcha and use "submit-form.com/DhrCyMyaP" because this form doesnt require a captcha i made it for trial purposes. Commented May 29, 2024 at 18:30
  • 1
    yea of course you're welcome. Thanks for making a new form, either it seems to work, at least on the http part. Right now, only issue I see is calling reset on currentTarget, which is null. I'll update answer to explain that also.
    – Max
    Commented May 29, 2024 at 18:38
  • 1
    EUREKA! What I have changed: I added "const responseData = await response.json(); console.log({ responseData });" these lines to give me the responseData, and responseData was coming empty ( "{"responseData": {}}" ). The fix: I created a new constant to make formData a plain object: formDataObject = Object.fromEntries(formData.entries()). I set formDataObject to be stringified like this: "body: JSON.stringify(formDataObject)". This time around, my form was being submitted to Formspark! Please edit your answer accordingly, thank you so much for your help. Commented May 30, 2024 at 4:35
1

All of the problems were about my form's on-submit action. Basically the main problem was that I didn't add correct headers (Content-type and Accept) inside "const response = ..." that Formspark expected my form to have, which is kind of embarassing. The second problem (which wasn't the one that I was facing but eventually was going to face) is that I didn't use formData as a plain Object and also didn't stringify it. And the final problem was not using a const value to reset() if the response was ok, which if you don't use a const to represent currentTarget, it would throw an error.

Here is what it should've looked like:

/* Other lines of code that have remained unchanged.*/

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
        const target = event.currentTarget

        event.preventDefault()
        if (!token) {
            // You don't need to include this if you are not using captcha
            // alert("Please verify that you are human.")
            // return
        }

        const formData = new FormData(event.currentTarget)
        const formDataObject = Object.fromEntries(formData.entries())
        console.log("Form Data: ", formDataObject)

        try {
            const response = await fetch("https://submit-form.com/form-id", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Accept: "application/json",
                },
                body: JSON.stringify(formDataObject),
            })
            console.log({ response })
            const responseData = await response.json()
            console.log({ responseData })
            if (response.ok) {
                setMessage("Form successfully submitted!")
                setIsSuccess(true)
                target.reset()
            } else {
                const errorText = await response.text()
                console.error("Error response: ", errorText)
                setMessage(
                    "There was a problem with your submission: " + errorText
                )
                setIsSuccess(false)
            }
        } catch (error) {
            console.error("Submission error: ", error)
            setMessage(
                "There was an error submitting the form: " + error.message
            )
            setIsSuccess(false)
        }
    }
/* Other lines of code that have remained unchanged.*/

Big thanks to Mr. Max

Not the answer you're looking for? Browse other questions tagged or ask your own question.