Prevent to send form if there is an error - javascript

A sandbox with my components : https://codesandbox.io/s/fancy-star-7gite
Hello, I've tried to code in React.js a contact form which check if values of inputs are good and send it to a mailbox via email.js. Sample of the validation's file(validateInfo.jsx):
if(!values.email) {
errors.email= "Enter an e-mail";
} else if (!/\S+#\S+\.\S+/.test(values.email)){
errors.email= "Enter a valid email";
}
But at the submission of the form even if the values are wrong, it sends the form. I want to prevent it.
To transport the form to a mailbox I've created a hook to manage the sending of the information in the form (useContact.jsx):
import {useState, useEffect} from 'react'
import emailjs from "emailjs-com";
import {template} from "./template";
import {userId} from "./userid";
export const useContact = (callback, validate) => {
const [values, setValues] = useState({
email:'',
emailConf:'',
userName:'',
phone:'',
})
const [errors, setErrors] = useState({})
const [isSubmitting, setIsSubmitting] = useState(false)
const handleChange = e =>{
const {name, value}= e.target
setValues({
...values,
[name]: value
})
}
function handleSubmit(e) {
e.preventDefault()
setIsSubmitting(true)
setErrors(validate(values))
emailjs.sendForm('gmail', template, e.target, userId)
.then((result) => {
console.log(result)
}, (errors) => {
console.log(errors);
});
e.target.reset();
localStorage.clear();
}
useEffect(() =>{
if(Object.keys(errors).length === 0 && isSubmitting)
{
callback();
}
},[errors])
return {handleChange, handleSubmit, values, errors};
}
The form (ContactInfo.jsx):
import './Contact.css';
import { useContact } from "./useContact";
import validate from './validateInfo';
export default function ContactInfo({submitForm}) {
const {handleChange, values, handleSubmit, errors}
= useContact(
submitForm,
validate
)
return (<>
<div className="contact-container">
<div className="contactForm" >
<form className="Contact" onSubmit={handleSubmit} >
<input
id="email"
name="email"
type="email"
value={values.email}
onChange={handleChange}
className="form-input"
placeholder="E-mail"
minLength= '5'
maxLength ='50'
autoComplete="off"/>
{errors.email && <p> {errors.email} </p>}
<input
name="emailConf"
id="emailConf"
type="email"
value={values.emailConf}
onChange={handleChange}
className="form-input"
minLength= '5'
maxLength='50'
autoComplete="off"
placeholder="E-mail confirmation"/>
{errors.emailConf && <p> {errors.emailConf} </p>}
<input
name="userName"
id="userName"
value={values.userName}
onChange={handleChange}
className="form-input"
maxLength='50'
autoComplete="off"
placeholder="Name"/>
<input
name="phone"
id="phone"
type="tel"
value={values.phone}
onChange={handleChange}
className="form-input"
maxLength='10'
autoComplete="off"
placeholder="Phone Number"/>
{errors.phone && <p> {errors.phone} </p>}
<textarea
className="message"
name="message"
maxLength='500'
placeholder="Write your message here..." />
<button className="submit-Btn" type="submit">
Send
</button>
</form>
</div>
</div>
</>
);
}
Thank you for helping me if you want more informations tell me =).

You are missing a condition in submit method. You are not validating whether there is any error in validation. It should be as follows. Updated the codesandbox as well
function handleSubmit(e) {
e.preventDefault();
setIsSubmitting(true);
setErrors(validate(values));
if (validate(values) === {}) {
emailjs.sendForm("gmail", template, e.target, userId).then(
(result) => {
console.log(result);
},
(errors) => {
console.log(errors);
}
);
e.target.reset();
localStorage.clear();
}
}

Related

onValidated is not a function when trying to subscribe user to mailchimp

I am using "react-mailchimp-subscribe" to integrate with MailChimp and enable users to signup to a mailchimp mailing list upon completing an submitting my form.
However, I get the error:
Uncaught TypeError: onValidated is not a function
When I trigger the function that should submit the form info to MailChimp.
Here is a sample of my code:
import MailchimpSubscribe from "react-mailchimp-subscribe";
...
const CustomForm = ({
className,
topOuterDivider,
bottomOuterDivider,
topDivider,
bottomDivider,
hasBgColor,
invertColor,
split,
status,
message,
onValidated,
...props
}) => {
...
const [email, setEmail] = useState('');
const [fullName, setFullName] = useState('');
const handleSubmit = (e) => {
console.log('handlesubmit triggered')
e.preventDefault();
email &&
fullName &&
email.indexOf("#") > -1 &&
onValidated({
EMAIL: email,
MERGE1: fullName,
});
}
...
<form onSubmit={(e) => handleSubmit(e)}>
<h3>
{status === "success"
? "You have successfully joined the mailing list"
: "Join our mailing list by completing the form below"
}
</h3>
<div className="cta-action">
<Input
id="email"
type="email"
label="Subscribe"
labelHidden hasIcon="right"
placeholder="Your email address"
onChange={e => setEmail(e.target.value)}
value={email}
isRequired
>
</Input>
<Input
id="name"
type="text"
label="Subscribe"
labelHidden hasIcon="right"
placeholder="Your full name"
onChange={e => setFullName(e.target.value)}
value={fullName}
isRequired
>
</Input>
<Input
type="submit"
formValues={[email, fullName]}
/>
</div>
</form>
This is the parent component passing down props to the above:
import React from "react";
import MailchimpSubscribe from "react-mailchimp-subscribe";
const MailchimpFormContainer = props => {
const postURL = `https://*li$%.list-manage.com/subscribe/post?u=${process.env.REACT_APP_MAILCHIMP_U}$id=${process.env.REACT_APP_MAILCHIMP_ID}`;
return (
<div>
<MailchimpSubscribe
url={postURL}
render={({ subscribe, status, message }) => (
<CustomForm
status={status}
message={message}
onValidated={ formData => subscribe(formData) }
/>
)}
/>
</div>
);
};

where to call the Axios post request in reactjs

I have a form,thats data are saved in the state to be sent to the backend server.
i am handling the form with handleSubmit function and useEffect hook, where the handleSubmit prevents the form from being submitted unless it calls the validation function, in the useEffect I check if there are any errors using if condition and then console.log my data.
now I want to post the data hold in the state -the state is sent as a props to me- but I am confused whether to put the request in the HandleSubmit function or in the useEffect inside the body of the if condition.
import react, { Component, useState, useEffect } from 'react';
import {useNavigate } from 'react-router-dom';
import axios from 'axios';
import './sign.css';
const SignA = (props) => {
const navigate = useNavigate();
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(false);
const handleSubmit = (err) => {
err.preventDefault();
setFormErrors(validate(props.data));
setIsSubmit(true);
}
useEffect(() => {
console.log(Object.keys(formErrors).length);
if (Object.keys(formErrors).length === 0 && isSubmit) {
console.log('console the props data', props.data)
//here is where I think the post request should be put
if (isSubmit) {
return (navigate('/profileadmin'))
}
}
}, [formErrors])
const validate = (values) => {
const errors = {};
const regex = /^[^\s#]+#[^\s#]+\.[^\s#]{2,}$/i;
if (!values.firstname) {
errors.firstname = 'firstname is required!';
}
if (!values.lastname) {
errors.lastname = 'lastname is required!';
}
if (!values.mobile) {
errors.mobile = 'mobile is required!';
}
if (!values.email) {
errors.email = 'email is required!';
} else if (!regex.test(values.email)) {
errors.email = 'this is not a valid email format!'
}
return errors;
}
return (
<div className='signup'>
<form onSubmit={handleSubmit} >
<div className="container">
<h1>Sign Up</h1>
<div className="name">
<div>
<input
type="text"
placeholder="First name"
name="firstname"
id='firstName'
value={props.data.firstname}
onChange={props.change}
/>
</div>
<div>
<input
type="text"
placeholder="Last name"
name="lastname"
value={props.data.lastname}
onChange={props.change}
/>
</div>
</div>
<p className='errorMsg'>{formErrors.firstname}</p>
<p className='errorMsg'>{formErrors.lastname}</p>
<br />
<div>
<input
type="text"
placeholder="Business mobile number"
name="mobile"
value={props.data.mobile}
onChange={props.change}
/>
<p className='errorMsg'>{formErrors.mobile}</p>
<br />
<input
type="text"
placeholder="Email Adress"
name="email"
value={props.data.email}
onChange={props.change}
/>
<p className='errorMsg'>{formErrors.email}</p>
<br />
</div>
</div>
<br />
<div className="checkbox">
<label>
<input type="checkbox" className="check" />i’ve read and agree with <a href="url" >Terms of service</a>
</label>
</div>
<div className="clearfix">
<button type="submit" className="signupbtn">Sign Up</button>
</div>
</div>
</form >
</div >
)
}
export default SignA;
this is the request
axios.post('', props.data)
.then(res => console.log('post res', res))
.catch(error => {
console.error('There was an error in post request!', error);
});
You don't necessarily need useEffect here.
Here is how you can implement such thing:
Declare a state to hold form values:
const [formData, setFormData] = useState({})
Declare function to set the state:
const handleChange = (name, value) => {
setFormData({...formData, [name]: value})
}
Input onChange to capture:
// handleChange has two parameters
<input
type="text"
placeholder="First name"
name="firstname"
id='firstName'
value={props.data.firstname}
onChange={(event) => handleChange('firstName', event.target.value)}
/>
function for calling post axios post request
const handleSubmit = () => {
//check for validations code here
// if validations are right then post request here
// this will give you all the fields like firstName: "", lastName: ""
let requestBody = {
...formData
}
axios.post("url", requestBody).then((res)=> {
//your code here
})
}

While implementing react-intl-tel-input in my react JS it give me errors

Hello I have started learning ReactJS and from last 1 week i stuck with a problem. I am using React with Firebase Phone Authentication. I want to use react-intl-tel-input for taking Phone input. I have installed the npm package and write the code as told in documentation. after running the code it takes the input correctly but after clicking on verify it say this number is not register but this number work perfectly with tag but not with this
please have a look on my code
import React from 'react'
import firebase from './firebase'
import 'firebase/auth';
import "./App.css";
import { getDatabase, ref, child, get } from "firebase/database";
import IntlTelInput from 'react-intl-tel-input';
import 'react-intl-tel-input/dist/main.css';
class Login extends React.Component {
state = {};
handlePhoneChange = (status, phoneNumber, country) => {
console.log({ phoneNumber });
this.setState({ phoneNumber });
};
handleChange = (e) => {
console.log (e)
const { name, value } = e.target
this.setState({
[name]: value
})
console.log (value)
this.setState({ phoneNumber: value }, () => {
console.log(this.state.phoneNumber);
});
}
configureCaptcha = () =>{
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.onSignInSubmit();
console.log("Recaptca varified")
},
// defaultCountry: "IN"
}
);
}
onSignInSubmit = (e) => {
e.preventDefault()
this.configureCaptcha()
const phoneNumber = this.state.mobile
console.log(phoneNumber)
const appVerifier = window.recaptchaVerifier;
const dbRef = ref(getDatabase());
get(child(dbRef, `Users/${phoneNumber}`)).then((snapshot) => {
if (snapshot.exists()) {
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then((confirmationResult) => {
window.confirmationResult = confirmationResult;
alert('An OTP has been sent to your registered mobile number')
localStorage.setItem("Phone_No", phoneNumber)
console.log(localStorage.getItem('Phone_No'));
}).catch((error) => {
console.error(error);
alert("Oops! Some error occured. Please try again.")
});
}
else {
alert('Sorry, this mobile number is not registered with us. Please use your registered mobile number.');
}
})
}
onSubmitOTP = (e) => {
e.preventDefault()
const code = this.state.otp
console.log(code)
window.confirmationResult.confirm(code).then((result) => {
// User signed in successfully.
const Users = result.user;
console.log(JSON.stringify(Users))
this.props.history.push("/home");
}).catch((error) => {
alert("You have entered wrong code")
});
}
render() {
return (
<>
<main>
<div className="img">
<img src="./55k-logo.png" alt="Company Logo" style={{ height: "80px", width: "200px" }} />
<br />
</div>
<fieldset>
<div className="Main-header">
<h1>Sign-In</h1>
<p>Limtless Water. From Unlimited Air.</p>
<form onSubmit={this.onSignInSubmit}>
<div id="sign-in-button"></div>
<label>Mobile Number</label> <br />
<IntlTelInput
containerClassName="intl-tel-input"
inputClassName="form-control"
name="mobile"
placeholder="Enter Your Number"
input
type="tel"
value={this.state.phoneNumber}
onPhoneNumberChange={this.handlePhoneChange}
/>
{/* <input type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange} /> */}
<div className="buttons">
<button type="submit">Verify</button>
</div>
</form>
<div>
<form onSubmit={this.onSubmitOTP}>
<label >Code</label> <br />
<input type="text" name="otp" placeholder="Enter six digit code" required onChange={this.handleChange} />
<div className="buttons" >
<button type="submit">Continue</button>
</div>
</form>
</div>
</div>
</fieldset>
</main>
</>
)
}
}
export default Login;
after running the code i got this message but my number is registered
But my code work perfectly with this
<input type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange} /> but i don't want to take input with normal input tag because here user have to type country code manually

How to integrate react-intl-tel-input-v2

While using react-intl-tel-input-v2 I was getting this error:-× TypeError: Cannot read properties of null (reading 'e') I have install the react-intl-tel-input-v2 from npm Try many things but nothing work if anyone know the solution please help Even if you know any other npm package which help me please suggest
I was getting the error in this part:-
handleChange = (e) => { const { name, value } = e.target this.setState({ [name]: value
This is my code
import React from 'react'
import firebase from './firebase'
import 'firebase/auth';
import "./App.css";
import { getDatabase, ref, child, get } from "firebase/database";
// import PhoneInput from 'react-phone-number-input'
// import $ from 'jquery';
// import intlTelInputUtils from 'jquery';
import ReactIntlTelInput from 'react-intl-tel-input-v2';
import 'intl-tel-input/build/css/intlTelInput.css';
class Login extends React.Component {
handleChange = (e) => {
const { name, value } = e.target
this.setState({
[name]: value
})
this.setState({ phoneNumber: value }, () => {
console.log(this.state.phoneNumber);
});
}
configureCaptcha = () =>{
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
this.onSignInSubmit();
console.log("Recaptca varified")
},
defaultCountry: "IN"
}
);
}
onSignInSubmit = (e) => {
e.preventDefault()
this.configureCaptcha()
const phoneNumber = this.state.mobile
console.log(phoneNumber)
const appVerifier = window.recaptchaVerifier;
const dbRef = ref(getDatabase());
get(child(dbRef, `Users/${phoneNumber}`)).then((snapshot) => {
if (snapshot.exists()) {
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then((confirmationResult) => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
alert('An OTP has been sent to your registered mobile number')
localStorage.setItem("Phone_No", phoneNumber)
console.log(localStorage.getItem('Phone_No'));
}).catch((error) => {
console.error(error);
alert("Oops! Some error occured. Please try again.")
});
}
else {
alert('Sorry, this mobile number is not registered with us. Please use your registered mobile number.');
}
})
}
onSubmitOTP = (e) => {
e.preventDefault()
const code = this.state.otp
console.log(code)
window.confirmationResult.confirm(code).then((result) => {
// User signed in successfully.
const Users = result.user;
console.log(JSON.stringify(Users))
this.props.history.push("/home");
}).catch((error) => {
alert("You have entered wrong code")
});
}
render() {
return (
<div className="Main-header">
<img src="./55k-logo.png" alt="Company Logo" style={{ height: "80px", width: "200px" }} />
<br />
<div>
<h2>Login Form</h2>
<p>Limtless Water. From Unlimited Air.</p>
<form onSubmit={this.onSignInSubmit}>
<div id="sign-in-button"></div>
{/* <PhoneInput */}
<label>Mobile Number</label> <br />
{/* for="phoneNumber" */}
<ReactIntlTelInput
type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange}
value={this.state.value}
// onChange={this.handleChange}
/>
{/* <input type="tel" id="phone" name="mobile" placeholder="Enter Your Number" required onChange={this.handleChange} /> */}
<div className="buttons">
<button type="submit">Submit</button>
</div>
</form>
</div>
<div>
<form onSubmit={this.onSubmitOTP}>
<label >Code</label> <br />
{/* for="code" */}
<input type="number" name="otp" placeholder="Enter The 6 Digit OTP" required onChange={this.handleChange} />
<div className="buttons" >
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
)
}
}
export default Login;

React: How to change form validation from onSubmit validation for all fields, to onBlur instant validation for each field in the form?

I have the form which is functioning great, but I need to make the validation instant (as soon as the user defocuses the field, I want the error message shown for that field only). At the moment I have a validation that shows error messages when the submit button is clicked:
Here is the code:
The useForm hook
import { useState, useEffect } from 'react';
const useForm = (callback, validate, post) => {
const [values, setValues] = useState(post || {});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
}, [errors]);
const handleSubmit = (event) => {
if (event) event.preventDefault();
console.log('values in useForm', values)
setErrors(validate(values))
setIsSubmitting(true);
};
const handleInputChange = (event) => {
event.persist();
setValues(values => ({ ...values, [event.target.name]: event.target.value }));
};
return {
handleInputChange,
handleSubmit,
values,
errors,
}
};
export default useForm;
The validate function
const validate = (values) => {
const errors = {};
if (!values.title) {
errors.title = 'Title is required'
} else if (values.title.length < 5) {
errors.title = 'Title must be at least 5 characters long'
}
if (!values.body) {
errors.body = "Blog body is required"
} else if (values.body.length < 2 || values.body.length > 20) {
errors.body = "Text has to be between 2 and 20 characters long"
}
if (!values.author) {
errors.author = "The author's name is required"
}
if (!values.number) {
errors.number = "A number is required"
}
if (!values.email) {
errors.email = 'Email is required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}
export default validate;
The form component
import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import useForm from '../hooks/useForm';
import { addPost } from '../actions';
import validate from '../helpers/validate';
const CreateForm = () => {
const dispatch = useDispatch();
const history = useHistory();
const { values, handleInputChange, handleSubmit, errors } = useForm(submit, validate)
const { title, body, author, number, email } = values;
function submit() {
console.log("No errors", values)
const post = values;
console.log('Submit form', post)
dispatch(addPost(post))
history.push('/');
}
return (
<div className="create">
<h2>Add a new blog</h2>
<form onSubmit={handleSubmit} noValidate>
<label>Blog title:</label>
<input
type="text"
required
name="title"
value={title || ""}
onChange={handleInputChange}
className={errors.title ? 'red-border' : ""}
/>
{errors.title && (<p className="danger">{errors.title}</p>)}
<label>Blog body:</label>
<textarea
required
name="body"
value={body || ""}
onChange={handleInputChange}
className={errors.body ? 'red-border' : ""}
/>
{errors.body && (
<p className="danger">{errors.body}</p>
)}
<label>Author:</label>
<input
type="text"
required
name="author"
value={author || ""}
onChange={handleInputChange}
className={errors.author ? 'red-border' : ""}
/>
{errors.author && (
<p className="danger">{errors.author}</p>
)}
<label>Number:</label>
<input
type="number"
required
name="number"
value={number || ""}
onChange={handleInputChange}
className={errors.number ? 'red-border' : ""}
/>
{errors.number && (
<p className="danger">{errors.number}</p>
)}
<label>Email:</label>
<input
type="text"
required
name="email"
value={email || ""}
onChange={handleInputChange}
className={errors.email ? 'red-border' : ""}
/>
{errors.email && (
<p className="danger">{errors.email}</p>
)}
<button>Save</button>
</form>
</div>
);
}
export default CreateForm;
As dicussed in the comments, you just need to call your setError when you want to update your error helpers. Here's a live example that flags an error if you type "error" in any of the fields: https://codesandbox.io/s/wispy-monad-3t8us?file=/src/App.js
const validateInputs = (e) => {
console.log("validating inputs");
if (e.target.value === "error")
setErrors({ ...errors, [e.target.name]: true });
else setErrors({ ...errors, [e.target.name]: false });
};
<input
type="text"
required
name="title"
value={values.title}
onChange={handleInputChange}
style={errors.title ? { border: "2px solid red" } : null}
onBlur={validateInputs}
/>
As you said in your question, you should call validate() in onBlur instead of in onSubmit.
So just add onBlur event in each of your input:
<input
type="number"
required
name="number"
onBlur={validate}
onChange={handleInputChange}
/>

Categories

Resources