Dealing with multiple boolean state variables for validation - reactjs

I'm building a form in React, and have a function to validate the email and password as provided by the user: if either do not pass validation, a state variable is flipped from true to false, and an error message is conditionally rendered:
State variables
const [isEmail, setEmail] = useState(true);
const [isPassword, setPassword] = useState(true);
Validation function
const validateEmailAndPassword = (email, password) => {
const emailRegEx =
/^(([^<>()\[\]\\.,;:\s#"]****************;
email.match(emailRegEx) ? setEmail(true) : setEmail(false);
password.length > 8 ? setPassword(true) : setPassword(false);
};
Error message
<p className="errors">{!isEmail ? errors.email : null}</p>
Instead of declaring each state variable individually, what would be the best way to declare them toegther, as I've done with the other inputs in my form?
Here's the complete file:
import { useState, useEffect } from "react";
const Form = () => {
const [{ email, password,colour }, setFormDetails] = useState({
email: "",
password: "",
colour: ""
});
const [isEmail, setEmail] = useState(true);
const [isPassword, setPassword] = useState(true);
const [isTigerChecked, setTigerChecked] = useState(false);
useEffect(() => {
document.title = 'Contact form'
},[])
const errors = {
password: "Password needs to contain 8 or more characters",
email: "Please enter a valid email",
};
const handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
setFormDetails((prevForm) => ({
...prevForm,
[name]: value,
}));
};
const validateEmailAndPassword = (email, password) => {
const emailRegEx =
/^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
email.match(emailRegEx) ? setEmail(true) : setEmail(false);
password.length > 8 ? setPassword(true) : setPassword(false);
};
const handleClick = (e) => {
e.target.checked ? setTigerChecked(true) : setTigerChecked(false)
}
const handleSubmit = (e) => {
e.preventDefault();
validateEmailAndPassword(email, password);
};
return (
<div className="form-container">
<p>Contact form</p>
<form onSubmit={handleSubmit}>
<input
className="form-element"
type="text"
placeholder="Enter email address"
name="email"
onChange={handleChange}
/>
<p className="errors">{!isEmail ? errors.email : null}</p>
<input
className="form-element"
type="password"
placeholder="Enter password"
name="password"
onChange={handleChange}
/>
<p className="errors">{!isPassword ? errors.password : null}</p>
<fieldset className="form-element">
<legend>Please select a colour</legend>
<select name="colour" id="colour" onChange = {handleChange}>
<option value="Blue">Blue</option>
<option value="Green">Green</option>
<option value="Red">Red</option>
<option value="Black">Black</option>
<option value="Brown">Brown</option>
</select>
</fieldset>
<fieldset className="form-element">
<legend>Please select your animals</legend>
<div className="checkbox">
<input type="checkbox" id="bear" name="bear" />
<label for="bear"> Bear</label>
<br></br>
<input
type="checkbox"
id="Tiger"
name="Tiger"
onClick={handleClick}
/>
<label for="Tiger"> Tiger</label>
<br></br>
<input type="checkbox" id="Snake" name="Snake" />
<label for="Snake"> Snake</label>
<br></br>
<input type="checkbox" id="Donkey" name="Donkey" />
<label for="Donkey"> Donkey</label>
<br></br>
</div>
</fieldset>
{isTigerChecked ? (
<textarea
id="tiger-type"
name="tiger-type"
rows="4"
cols="50"
placeholder="Please enter type of Tiger"
/>
) : null}
<button type="submit">Submit</button>
</form>
</div>
);
};
export default Form;

I would do this:
Get rid of the isEmail and isPassword states, and make an errors state with an empty object as the initial value.
const [errors, setErrors] = useState({});
and change the name of the errors obj that holds the error messages to errorMessages
const errorMessages = {
password: "Password needs to contain 8 or more characters",
email: "Please enter a valid email",
}
Within your validate function, make an errorsObj variable assigned to an empty object. If email doesn't match, update the errorsObj so that the email key is assigned to the email error string from your errors object (same for password).
const errorsObj = {};
if(!email.match(emailRegEx)) errorsObj['email'] = errors['email']
if (!(password.length > 8)) errorsObj['password'] = errors['password']
setErrors(errorsObj);
now when conditionally rendering the error messages, do this
{!!errors['email'] && <p className="errors">{errors['email'}</p>}
{!!errors['password'] && <p className="errors">{errors['password'}</p>}
you could also adjust your handleChange function to update errors properly
const handleChange = (e) => {
e.preventDefault();
const { name, value } = e.target;
setFormDetails((prevForm) => ({
...prevForm,
[name]: value,
}));
if (errors[name]) {
setErrors(prevState => {
delete prevState[name];
return prevState;
}
}
};

Related

Why do I need a separate handler from checked input?

Unable to get the value of the checked input (true or false).
function Login() {
const [inputs, setInputs] = useState({});
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
setInputs(values => ({
...values,
[name]: value
}));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log({inputs});
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="mail" value={inputs.mail || ""} onChange={handleChange}/>
<input type="password" name="pass" value={inputs.pass || ""} onChange={handleChange}/>
<div>
<input type="checkbox" name="check" value={inputs.check || false} onChange={handleChange}/>
<label>Remember me</label>
</div>
<button type="submit">Submit</button>
</form>
);
}
export default LoginGoogle
Tried
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
const check = event.target.checked;
setInputs(values => ({
...values,
[name]: value || check
}));
};
For
<input type="checkbox" name="check" value={inputs.checked} onChange={handleChange}/>
And
<input type="checkbox" name="check" checked={inputs.checked} onChange={handleChange}/>
It works, but I am certain I am going about it wrong. Tutorials seem to concentrate on input objects of similar key-values, e.g. multiple checkboxes, multiple text input, and so on.
The reason is that checkbox values never change, only their checked property does.
Ideally, you'd use a different change handler
const onCheckboxChange = ({ target: { name, checked } }) => {
setInputs((prev) => ({
...prev,
[name]: checked,
}));
};
return (
<input
type="checkbox"
name="check"
checked={inputs.check}
onChange={onCheckboxChange}
/>
);

Localstorage in reactjs when it get reloaded key is present values are missing

import { useState } from "react";
function App() {
const initialValues = { username:"", password: "" };
const [formValues, setFormValues] = useState(initialValues);
const [formErrors, setFormErrors] = useState({});
const [isSubmit, setIsSubmit] = useState(
JSON.parse(localStorage.getItem("data")) ? true : false
);
const handleChange = (e) => {
const { name, value } = e.target;
setFormValues({ ...formValues, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
setFormErrors(validate(formValues));
setIsSubmit(true);
};
const validate = (values) => {
const errors = {};
if (!values.username) {
errors.username = "Username is required!";
}
if (!values.password) {
errors.password = "Password is required";
} else if (values.password.length < 4) {
errors.password = "Password must be more than 4 characters";
} else if (values.password.length > 8) {
errors.password = "Password cannot exceed more than 10 characters";
}
return errors;
};
const logout = () => {
setIsSubmit(false);
localStorage.clear();
};
return (
<div className="container">
{Object.keys(formErrors).length === 0 && isSubmit ? (
<>
{localStorage.setItem("data", JSON.stringify(formValues))}
<div>
hello {JSON.parse(localStorage.getItem("data")).username} ,Signed
in successfully
</div>
<button onClick={logout}>LOG OUT</button>
</>
) : (
<form onSubmit={handleSubmit}>
<div>
<div>
<label>Username</label>
<input
type="text"
name="username"
placeholder="Username"
value={formValues.username}
onChange={handleChange}
/>
</div>
<p>{formErrors.username}</p>
<div>
<label>Password</label>
<input
type="password"
name="password"
placeholder="Password"
value={formValues.password}
onChange={handleChange}
/>
</div>
<p>{formErrors.password}</p>
<button>Submit</button>
</div>
</form>
)}
</div>
);
}
export default App;
once data stored in storage
when it get reloaded keys are present but value missing
when form is getting reloaded the data which is stored in the from of object in localStorage the values are getting missed while key are present.when form is getting reloaded the data which is stored in the from of object in local Storage the values are getting missed while key are present.
You need to set the handleSubmit func instead of returning the data to the local storage, as you can see in the code below.
const handleSubmit = (e) => {
e.preventDefault();
setFormErrors(validate(formValues));
setIsSubmit(true);
localStorage.setItem("data", JSON.stringify(formValues));
};
Remove this line {localStorage.setItem("data", JSON.stringify(formValues))} in return method.

How to validate email and password using react hooks?

I am getting state values while clicking submit button but I am unable to do the validation for my login form and how to display the error messages below the input field when I enter my input wrong or empty. please give me a solution to this.Thanks in advance.
const Login = () => {
const [state, setState] = useState({
email: "",
password: ""
});
const handleChange = (e) => {
const {id, value} = e.target
setState(prevState => ({
...prevState,
[id]: value
}))
}
const handleSubmitClick = (e) => {
e.preventDefault();
console.log("Authenticated",state);
}
return(
<>
<div className="container">
<div className="title">
<form onSubmit={handleSubmitClick}>
<div className="form-group">
<input
type="email"
className="email"
placeholder="Email"
value={state.email}
onChange={handleChange}/>
</div>
<div className="form-group">
<input
type="password"
className="password"
placeholder="Password"
value={state.password}
onChange={handleChange}/>
</div>
<button type="submit" className="button">Enter</button>
</form>
</div>
</div>
</>
)
}
export default Login;
If you want to perform client-side validation, you can create hook like this:
const useEmailValidation = (email) => {
const isEmailValid = /#/.test(email); // use any validator you want
return isEmailValid;
};
And then you can use this hook in your form component:
...
const isEmailValid = useEmailValidation(state.email);
const isPasswordValid = usePasswordValidation(state.password);
const isFormValid = isEmailValid && isPasswordValid;
return (
...
<input
className={classNames({ 'invalid': !isEmailValid })}
type="email"
value={state.email}
onChange={handleChange}
/>
{!isEmailValid && 'Some error message'}
<button type="submit" disabled={!isFormValid} className="button">Enter</button>
...
);
...
Your validator hook can return validation message instead of boolean, like:
const useEmailValidation = (email) => {
if (!email || email.length === 0) {
return 'Email cannot be empty';
}
const isEmailValid = /#/.test(email); // use any validator you want
if (!isEmailValid) {
return 'Invalid email provided';
}
return null;
};
Also it is a good practice to show validation message only after field was focused before and after user tried to submit the form.
Formik is a great plugin that will help you perform form validation. The examples are also quite clear.
Or you could do something like this:
const Login = () => {
const [error, setError] = useState(null);
const [state, setState] = useState({
email: '',
password: '',
});
const validateEmail = (email) => {
const re =
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
};
const handleChange = (e) => {
const { id, value } = e.target;
setState((prevState) => ({
...prevState,
[id]: value,
}));
};
const handleSubmitClick = (e) => {
e.preventDefault();
if (!validateEmail(state.email)) {
setError('Invalid Email');
}
if (state.password.length < 8) {
setError('Password must be at least 8 chars long');
}
if (!error) {
// No errors.
}
};
return (
<>
<div className='container'>
<div className='title'>
{error && <div style={{ color: 'red' }}>{error}</div>}
<form onSubmit={handleSubmitClick}>
<div className='form-group'>
<input
type='email'
className='email'
placeholder='Email'
value={state.email}
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
type='password'
className='password'
placeholder='Password'
value={state.password}
onChange={handleChange}
/>
</div>
<button type='submit' className='button'>
Enter
</button>
</form>
</div>
</div>
</>
);
};
export default Login;
For an empty validation you can check it preventing the submit if the field is empty, like
const handleSubmitClick = (e) => {
e.preventDefault();
if(email.trim() === '' || password.trim() === ''){
//Add a h1 or section with the error message
}else{
console.log("Authenticated",state);
}
}
As long as the email field type is equal to email, which is your case, the browser should give an alert if the string is not an email. ("user#example.com")

React Hooks, state not updating

I m new to react and js.
import React from "react";
import { useState, useEffect } from "react";
const Forminput = () => {
const [errors, setErrors] = useState({}); //state values
const [values, setValues] = useState({
username: "",
email: "",
}); //state values
const change = (e) => {
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
};
const submitChange = (e) => {
e.preventDefault();
console.log(values);
setErrors(validation(values));
console.log(errors);
};
const validation= (values) => {
if (!values.username.trim()) {
errors.username = "Username required";
}
if (!values.email) {
errors.email = "Email required";
} else if (!/\S+#\S+\.\S+/.test(values.email)) {
errors.email = "Email address is invalid";
}
return errors;
};
useEffect(() => {}, [errors]);
return (
<div>
<h2>Create Your Form </h2>
<form action="" className="form-inputs" onSubmit={submitChange}>
<div className="username">
<label htmlFor="username" className="username">
Username
</label>
<input
id="username"
type="text"
className="username"
name="username"
onChange={change}
value={values.username}
/>
{errors.username && <p>{errors.username}</p>}
</div>
<div className="email">
<label htmlFor="email" className="email">
email
</label>
<input
id="email"
type="text"
name="email"
className="email"
onChange={change}
value={values.email}
/>
{errors.email && <p>{errors.email}</p>}
</div>
<input type="submit" className="signup" />
</form>
</div>
);
};
setErrors state is not updating.
Two fields username and email. when entered username and not email first for time the errors state is updating.
when entered again right email then press submit it is not updating the error state but updating the values state. i tried using useeffect() but not working.
i new here could you please me.
I want to update the errors state too when i update values state. is this right way or help me
This happens because you're mutating the errors object in validation and setting the same errors object when you call setErrors in submitChange. Since errors is the same object reference, this doesn't trigger a re-render. You'll need to return a new object from validation:
const validation= (values) => {
const result = {};
if (!values.username.trim()) {
result.username = "Username required";
}
if (!values.email) {
result.email = "Email required";
} else if (!/\S+#\S+\.\S+/.test(values.email)) {
result.email = "Email address is invalid";
}
return result;
};

How to write validations for email and password using react hooks

I am working on React in that project I am trying to write validations for Email and password by
using react hooks But I don't know how to start it write it so please help to achieve this.
What I want exactly is I hard coded Email and Password in my code. Now Just I want to write validations for Email and Password. What I want exactly is If I enter Incorrect Email and Correct password then in Validations it have to show only please enter valid email address.
If I enter correct Email then it has to show please enter correct password. If both are wrong means then it has to show validations please enter correct Email and Password.
This is my code
import React, { useState } from 'react';
import './Login.css';
const Login = () => {
const [loginData, setLoginData] = useState(null)
const loginCredentials = () => {
if (loginData.email === 'john#gmail.com' && loginData.password === 'christina') {
} else {
}
}
const handleChange = ({ target }) => {
const { name, value } = target
const newData = Object.assign({}, loginData, { [name]: value })
setLoginData(newData)
}
const handleSubmit = (e) => {
e.preventDefault()
loginCredentials()
}
return (
<div className='container'>
<div className='row justify-content-center'>
<div className='col-4'>
<div className='mainform mt-3'>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="email">Email</label>
<input type="email" name='email' className="form-control" id="email" onChange={handleChange}></input>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input type="password" name='password' className="form-control" id="password" onChange={handleChange}></input>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
)
}
export default Login
Try saving the errors in an array and check the length of array at the end.
const [errors,setErrors]=useState([]);
.
.
.
if(username !='john#gmail.com')
{
setErrors([...errors,{error:'Invalid username'}])
}
if(password!='something')
{
setErrors([...errors,{error:'Invalid password'}])
}
Now inside of the component
{errors?errors.map(elem=>elem.error):null}
You can create a state for the error message and change its value based on what combination of email and password is incorrect. If
const [errorMessage, setErrorMessage] = useState('')
const loginCredentials = () => {
if (loginData.email !== 'john#gmail.com' && loginData.password !== 'christina') {
setErrorMessage('email and pw are both incorrect')
} else if (loginData.email !== 'john#gmail.com' {
setErrorMessage('email is incorrect')
} else if {.....etc
}
then put {errorMessage} in your jsx where you want it to show up
You can use nested if else
const [error, setError] = useState("");
const loginCredentials = () => {
if (loginData.email === "john#gmail.com") {
if (loginData.password === "christina") {
setError("");
return true;
}
else
{
setError("Incorrect Password");
return false;
}
} else {
setError("Incorrect Email and password");
return false;
}
};
To show the error you can put a div somewhere in the form which will only show when there is error.
{error && <div>{error}</div>}
First of all, your input values are not controlled elements. learn about 'controlled component'.
There are lots of ways you can achieve this.
I think you can add a custom hook to handle onChange and reset handler.
custom hook:
import { useState } from 'react';
const useInputState = (initialValues) => {
const [state, setstate] = useState(initialValues);
const onChangeHandler = (e) => setstate({
...state,
[e.target.name]: e.target.value,
});
const reset = (field) => {
if (field) {
setstate({
...state,
[field]: initialValues[field],
});
} else {
setstate({
...initialValues,
});
}
};
return [state, onChangeHandler, reset];
};
export default useInputState;
Then consume that hook in your component.
import React, { useState } from 'react';
import UseInputState from '../hooks/useInputState';
const App = () => {
const [value, onChangeHandler, reset] = UseInputState('');
console.log({ value });
const [emailAlert, setEmailAlert] = useState(null);
const [passwordAlert, setPasswordAlert] = useState(null);
const loginCredentials = () => {
setEmailAlert(null);
setPasswordAlert(null);
const { email, password } = value;
if (email !== 'john#gmail.com') {
setEmailAlert('Please Enter valid Email Address');
}
if (password !== 'christina') {
setPasswordAlert(' please enter correct password');
}
reset('');
if (password === 'christina' && email === 'john#gmail.com') {
setEmailAlert(null);
setPasswordAlert(null);
}
};
const handleSubmit = (e) => {
e.preventDefault();
loginCredentials();
};
return (
<div className='container'>
<div className='row justify-content-center'>
<div className='col-4'>
<div className='mainform mt-3'>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<label htmlFor='email'>Email</label>
<input
type='email'
name='email'
className='form-control'
id='email'
value={value.email || ''}
onChange={onChangeHandler}
></input>
</div>
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
type='password'
name='password'
className='form-control'
id='password'
value={value.password || ''}
onChange={onChangeHandler}
></input>
</div>
<button type='submit' className='btn btn-primary'>
Submit
</button>
</form>
{emailAlert && <div>{emailAlert}</div>}
{passwordAlert && <div>{passwordAlert}</div>}
</div>
</div>
</div>
</div>
);
};
export default App;

Resources