skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    Solution

    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


  2. 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!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search