The validation is working correctly, however when the user fails the validation the default action is still carried out. So validation fails and the joke is still returned. If validation fails, the Joke should not be returned. I have tried to use preventDefault but no luck.
import React, { useState } from "react";
function Search() {
const [joke, setJoke] = useState()
const [firstname, setFN] = useState("sharoze")
const [lastname, setLN] = useState("khan")
const newJoke = (first, last) => {
fetch(`http://api.icndb.com/jokes/random?firstName=${first}&lastName=${last}`)
.then(result => result.json())
.then(result2 => {
console.log(result2)
setJoke(result2.value.joke)
})
}
function validateForm() {
var firstname = document.getElementsByName("firstname")[0].value;
var lastname = document.getElementsByName("lastname")[0].value;
if (firstname === "" && lastname === "") {
alert("Please enter atleast one name");
}
else if (!(/^[a-zA-Z]+$/.test(firstname + lastname))) {
alert("'Only alphabets allowed'");
}
}
return (
<div className="jokeForm" >
<form name="searchForm" >
<input type="text" name="firstname" placeholder="First name" value={firstname} onChange={(e) => setFN(e.target.value)} />
<input type="text" name="lastname" placeholder="Last name" value={lastname} onChange={(e) => setLN(e.target.value)} />
</form>
<button id="button" onClick={(e) => { validateForm(newJoke(firstname, lastname)); return false; }}>click here for a personalised chuckle</button>
<h3>{joke}</h3>
</div >
)
}
export default Search;
sorry the code has been a little butchered!
The main problem I can see is that you don't pass event argument from onClick to your validation function, hence preventDefault() function is not working.
Try to pass event argument to validation function and then use this argument in function to apply preventDefault().
--Update--
You don't need to use preventDefault() at all. Just modify your validation function to call new joke only if validation is passed.
import React, { useState } from "react";
function Search() {
const [joke, setJoke] = useState()
const [firstname, setFN] = useState("sharoze")
const [lastname, setLN] = useState("khan")
const newJoke = (first, last) => {
fetch(`http://api.icndb.com/jokes/random?firstName=${first}&lastName=${last}`)
.then(result => result.json())
.then(result2 => {
console.log(result2)
setJoke(result2.value.joke)
})
}
function validateForm() {
var firstname = document.getElementsByName("firstname")[0].value;
var lastname = document.getElementsByName("lastname")[0].value;
if (firstname === "" && lastname === "") {
alert("Please enter atleast one name");
return false;
}
else if (!(/^[a-zA-Z]+$/.test(firstname + lastname))) {
alert("'Only alphabets allowed'");
return false;
}
newJoke(firstname, lastname);
}
return (
<div className="jokeForm" >
<form name="searchForm" >
<input type="text" name="firstname" placeholder="First name" value={firstname} onChange={(e) => setFN(e.target.value)} />
<input type="text" name="lastname" placeholder="Last name" value={lastname} onChange={(e) => setLN(e.target.value)} />
</form>
<button id="button" onClick={() => validateForm()}>click here for a personalised chuckle</button>
<h3>{joke}</h3>
</div >
)
}
export default Search;
Related
I'm trying to make a simple form that has validation and after the validation is success send a mail with Email.js. I'm using only email.js. I'm trying to set an if statement that would check that my formErrors object is empty and if it is to "fire" the mail sending. But so far I've tried a few methods but they all send mail even if the form hasn't passed the validation (formErrors object has value). My code here (I removed my service, template and key from .sendForm) with the latest check I attempted (Object.keys(formErrors).length === 0) that still doesn't prevent the mail to be sent:
import React, { useState, useRef } from "react";
import emailjs from "#emailjs/browser";
import "./App.css";
function App() {
const initialValues = { user: "", email: "", pass: "" };
const [formValues, setFormValues] = useState(initialValues);
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
};
const form = useRef();
const handleSubmit = (e) => {
e.preventDefault();
setFormErrors(validate(formValues));
setIsSubmit(true);
if (Object.keys(formErrors).length === 0) {
emailjs
.sendForm(
"",
"",
form.current,
""
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
}
};
const validate = (v) => {
const errors = {};
const regex = /^[^\s#]+#[^\s#]+\.[^\s#]{2,}$/i;
if (!v.user) {
errors.user = "This field can't be empty";
}
if (!v.email) {
errors.email = "This field can't be empty";
} else if (!regex.test(v.email)) {
errors.email = "Incorrect email";
}
if (!v.pass) {
errors.pass = "This field can't be empty";
} else if (v.pass.length < 6) {
errors.pass = "Password needs to be at least 6 characters";
}
return errors;
};
return (
<div className="container">
{Object.keys(formErrors).length === 0 && isSubmit ? (
<div className="title">Success</div>
) : (
<div className="title">Fill the form</div>
)}
<form ref={form} onSubmit={handleSubmit}>
<h1>Login Form</h1>
<hr />
<div className="form">
<div className="field">
<label>Username</label>
<input
type="text"
name="user"
placeholder="Username"
value={formValues.user}
onChange={handleChange}
/>
</div>
<p>{formErrors.user}</p>
<div className="field">
<label>Email</label>
<input
type="text"
name="email"
placeholder="Email"
value={formValues.email}
onChange={handleChange}
/>
</div>
<p>{formErrors.email}</p>
<div className="field">
<label>Password</label>
<input
type="password"
name="pass"
placeholder="Password"
value={formValues.pass}
onChange={handleChange}
/>
</div>
<p>{formErrors.pass}</p>
<button className="btn">Submit</button>
</div>
</form>
</div>
);
}
export default App;
I guess your issue related with non async behaviour of states. In handleSubmit function you set formErrors and right after you are checking it in conditional statement. If you replace your
if (Object.keys(formErrors).length === 0)
with
if (Object.keys(validate(formValues)).length === 0)
it should work as expected.
Another solution would be adding this in the beginning of handleSubmit function and it will not enter to any other logic of the submit
if (Object.keys(validate(formValues)).length > 0) {
return
}
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
})
}
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}
/>
When I use correct email and password it works fine.
But when email and password are incorrect, I get: Can't read property 'email' of undefined error. I want to handle it in the if/else section.
Please help. Thanks
const {customers} = useContext(customersContext)
const [customerEmail, setCustomerEmail] = useState("")
const [password, setPassword] = useState("")
const history = useHistory();
const currentUser = customers.find((s, index)=> s.email === customerEmail)
const handleLogin = (e) => {
e.preventDefault()
if(currentUser.email === customerEmail && currentUser.password === password)
{
localStorage.setItem("customers", currentUser.id)
history.push("/home")
props.togle()
}
else if(typeof currentUser.email === undefined)
{
window.alert("email does not match")
}
else {
window.alert("No match found")
}
}
return (
<div>
<Form>
<FormGroup>
<Label for="email">customerEmail</Label>
<Input type="email" name="email" onChange={(e) => setCustomerEmail(e.target.value)}/>
</FormGroup>
<FormGroup>
<Label for="password">Password</Label>
<Input type="password" name="password" onChange={(e) => setPassword(e.target.value)} />
</FormGroup>
<Button onClick={handleLogin}> Submit </Button>
</Form>
</div>
)
In general the logicals condition for handleLogin method may look so:
if (currentUser === undefined) {
window.alert("No match found")
// You shouldn't give a user a tip of what part in the login/password combination doesn't match, because it will make the brute forcing attempts way much easier
} else if (currentUser.email !== customerEmail || currentUser.password !== password) {
window.alert("email or password does not match")
} else {
localStorage.setItem("customers", currentUser.id)
history.push("/home")
props.togle()
}
I hope that you do not rely on this code on frontend. Because it is absolutely insecure to verify logins and
passwords in this manner =)
This is a React form. I have been trying to display Hello {firstName} {lastName} after the user gives the inputs and clicks the submit button . However, it is not recording the inputs properly and not displaying it correctly after the submit button is clicked. Please help!!!
import React, { useState } from 'react';
function App() {
const [newName, setNewName] = useState({
fName: '',
lName: ''
});
const [fullName, setFullName] = useState(newName);
function handleOnSubmit(event) {
console.log(newName);
setFullName(newName);
event.preventDefault();
}
function handleOnChange(event) {
console.log(newName);
var { value, name } = event.target;
setNewName((prevValue) => {
if (name === 'fName')
return {
fName: value,
lName: prevValue.lName
};
else
return {
fName: prevValue.fName,
lName: value
};
});
}
return (
<div className='container'>
<h1>
Hello {fullName.fName} {fullName.lName}
</h1>
<form onSubmit={handleOnSubmit}>
<input
name='fName'
placeholder='First Name'
onChange={handleOnChange}
value={fullName.fName}
/>
<input
name='lName'
placeholder='Last Name'
onChange={handleOnChange}
value={fullName.lName}
/>
<button type='submit'>Submit</button>
</form>
</div>
);
}
export default App;
The problem was event handling on the input boxes. In value you binded fullName.fName and fullName.lName , but onChange you are updating the state of newName and the state of the fullName is only getting changed when you click submit . Please update the form code as below. It should work for you !
<form onSubmit={handleOnSubmit}>
<input
name="fName"
placeholder="First Name"
onChange={handleOnChange}
value={newName.fName}
/>
<input
name="lName"
placeholder="Last Name"
onChange={handleOnChange}
value={newName.lName}
/>
<button type="submit">Submit</button>
</form>
Try using this:
We are going to create an Input custom hook.
useInput.js file
//input custom hook
import { useState } from "react";
function useInput(initialValue = "") {
const [value, setValue] = useState(initialValue);
const reset = () => {
setValue(initialValue);
};
const bind = {
value,
onChange: (e) => setValue(e.target.value),
};
return [value, bind, reset];
}
export default useInput;
This is how you can use this Input custom hook:
import React from "react";
import useInput from "../hooks/useInput";
function Test() {
const [firstName, bindFirstName, resetFirstName] = useInput("");
const [lastName, bindLastName, resetLastName] = useInput("");
const handleSubmit = (e) => {
e.preventDefault();
alert(`hello ${firstName} ${lastName}`); // you can change this as per your requirement
resetFirstName();
resetLastName();
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" {...bindFirstName} />
<input type="text" {...bindLastName} />
<button value="submit" type="submit">
Submit
</button>
</form>
</div>
);
}
export default Test;