DEV Community

loading...

[Solved]Data is sending even if validation requirements are not valid, whats the best way to approach this?

andrpaul profile image AndrPaul Updated on ・2 min read

So I've set up a custom react hook form with a simple validation, the data from the form is sent to my Gmail, but I receive emails on button submit even if the fields are empty, I've tried a few methods to fix this but nothing seems to work, code looks like this:

Form.js

import React from "react";
import useForm from "./useForm";
import validate from "./validate";


const Form = () => {
    const { handleChange, handleSubmit, values, errors } = useForm(
        submit,
        validate
    );


    function submit() {
        alert("Submitted Succesfully");
    }

    return (
        <div>

            <form onSubmit={handleSubmit} noValidate>
                <div className="inputField">
                    <input className={`${errors.email && "inputError"}`} name="email" type="email" placeholder="Your email *" value={values.email} onChange={handleChange} />
                    {errors.email && <p className="error">{errors.email}</p>}
                </div>
                <div className="inputField">
                    <input className={`${errors.email && "inputError"}`} name="name" type="text" placeholder="Your name *" value={values.name} onChange={handleChange} />
                    {errors.email && <p className="error">{errors.name}</p>}
                </div>
                <div className="inputField">
                    <input className={`${errors.email && "inputError"}`} name="subject" type="text" placeholder="Subject *" value={values.subject} onChange={handleChange} />
                    {errors.email && <p className="error">{errors.subject}</p>}
                </div>
                <div className="inputField">
                    <p className="reqTxt"> * = Required</p>
                    <textarea className={`${errors.email && "inputError"}`} name="description" placeholder="Type your message here *" value={values.description} onChange={handleChange} rows="15" cols="80"></textarea>
                    {errors.email && <p className="error">{errors.description}</p>}
                </div>


                <button className="btn" type="submit">Send message</button>
            </form>
        </div>
    );
};

useForm.js

import { useState, useEffect } from "react";
import axios from 'axios';

const useForm = (callback, validate) => {
  const [values, setValues] = useState({ email: '', name: '', subject: '', description: '' })
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);


  const handleChange = event => {
    const { name, value } = event.target;
    setValues({
      ...values,
      [name]: value
    });
  };


  const handleSubmit = (event) => {
    event.preventDefault();
    setErrors(validate(values));
    setIsSubmitting(true);

    const {email,name,subject,description} = values;

    axios.post('http://localhost:8080/sendme', {

            email,
            name,
            subject,
            text: description
        })
  };

  useEffect(() => {
    if (Object.keys(errors).length === 0 && isSubmitting) {
      callback();
    }
  }, [callback,isSubmitting,errors]);

  return {
    handleChange,
    handleSubmit,
    values,
    errors
  };
};

export default useForm;

validate.js

export default function validate(values) {
    let errors = {};
    if (!values.email) {
        errors.email = "Email is required";
    } else if (!/\S+@\S+\.\S+/.test(values.email)) {
        errors.email = "Email address is invalid, ex: your@email.com";
    }
    if (!values.name) {
        errors.name = "Please type in your name.";
    }
    if (!values.subject) {
        errors.subject = "Please don't leave the subject field empty.";
    }
    if (values.description.length < 20) {
        errors.description = "Your message needs to be more than 20 characters.";
    }
    return errors;
}

What is the best way to approach this (only send the data after the form validation = true or something)? I'd also like to know how to display a "success" message after the form is submitted successfully.

I'd appreciate it if anyone could give me an advice concerning this or can provide a solution.

Discussion (2)

pic
Editor guide
Collapse
_gerald20 profile image
Gerald Stewart • Edited

This might not be the optimal solution, but I would check if the return value from the validate function equals 0 in length, I know you are already doing this, but you are doing it after you send the API request. I would also recommend the use of promise chaining (developer.mozilla.org/en-US/docs/W...) for handling success and error cases on async functions such as API requests. As for displaying success or error messages, you could set some state variable just like you did with the error messages or what I have commonly used is toastr (github.com/CodeSeven/toastr) for easy popup notifications.

import { useState, useEffect } from "react";
import axios from 'axios';

const useForm = (callback, validate) => {
  const [values, setValues] = useState({ email: '', name: '', subject: '', description: '' })
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);


  const handleChange = event => {
    const { name, value } = event.target;
    setValues({
      ...values,
      [name]: value
    });
  };


  const handleSubmit = (event) => {
    event.preventDefault();
    setErrors(validate(values)).then((errors) => {
    if(errors.length === 0){
       const {email,name,subject,description} = values;
       axios.post('http://localhost:8080/sendme', {

            email,
            name,
            subject,
            text: description
        }).then((res) => {
        console.log('Display your success here!');
        setIsSubmitting(false);
        }).catch((e) => {
        console.log('Display your error here!');
        setIsSubmitting(false);
        });
        };
    });
  };

  useEffect(() => {
    if (Object.keys(errors).length === 0 && isSubmitting) {
      callback();
    }
  }, [callback,isSubmitting,errors]);

  return {
    handleChange,
    handleSubmit,
    values,
    errors
  };
};

export default useForm;

Another solution would be to move the axios.get call to the useState call, after you have validated that errors is equal to 0, not sure what the optimal solution is here

(I haven't checked if this code will run, let me know if it does not work)

Collapse
andrpaul profile image
AndrPaul Author

Thank you, since I posted this issue I switched to "react-hook-form" which worked like a charm, but I can see was wrong here, and used toastr to display my success message as you said and that saved me a lot of time.