I have some front-end validation for formatting some inputs on a form. Currently my errors message print to the console. I would like it so that these error message became the innerHTML of a heading in the component.
I have already tried assigning the innerHTML with document.getElementByID
but it hasnt worked. Ive additionally tried defining the error
variable outside of scope or adding it between the h2 tags as {error}
I would like any suggestions to make these error messages display as text on the front-end rather than being printed to console as they are now. The blank h2 element is the element im trying to target the error text towards.
import React, {useState} from 'react';
import {useNavigate} from 'react-router-dom';
import axios from 'axios';
import Helmet from 'react-helmet';
import Button from '@mui/material/Button';
export default function Register() {
const navigate = useNavigate();
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const inputs = [
{
placeholder: 'First name',
setState: setFirstName
},
{
placeholder: 'Last name',
setState: setLastName
},
{
placeholder: 'Email',
setState: setEmail
},
{
placeholder: 'Enter a password',
setState: setPassword,
},
{
placeholder: 'Confirm password',
setState: setConfirmPassword,
},
]
//Insert into database api request
const insertRow = () => {
axios.post('/users/insert', {
firstName: firstName,
lastName: lastName,
email: email,
password: password,
})
};
//#region Validation
const atSymbol = '@';
//Checks for numbers in string
function containsNumber(str) {
return /[0-9]/.test(str);
}
//Checks for capital in string
function containsCapital(str) {
return /[A-Z]/.test(str);
}
const submitHandler = (e) => {
e.preventDefault(); //Prevents page refresh
let error = ''
//If no @ symobol in email
if (!email.includes(atSymbol)) {
error = 'Please enter a valid email';
console.log(error);
return;
}
//If password doesn't contain a capital
if (!containsCapital(password)) {
error = 'Password must contain at least one uppercase letter';
console.log(error);
return;
}
//If password doesn't contain a number
if (!containsNumber(password)) {
error = 'Password must contain at least one number';
console.log(error);
return;
}
//If password is less than 8 characters
if (password.length < 8) {
error = 'Password must be at least 8 characters long';
console.log(error);
return;
}
//If passwords don't match
if (confirmPassword !== password) {
error = 'Passwords do not match';
console.log(error);
return;
}
//If all validation passes
insertRow();
navigate('/login');
}
//#endregion
return (
<>
<Helmet>
<title>Title | Register</title>
</Helmet>
<div className="pt-36 sm:pt-44 pb-20 md:pb-48 max-w-[1200px] mx-5 lg:mx-auto space-y-5">
<div className="bg-red-300 max-w-[500px] p-1 mx-auto">
<h2 className="text-lg text-red-900 font-semibold"></h2>
</div>
<h1 className="text-2xl font-semibold text-center">Register</h1>
<form onSubmit={submitHandler} className="flex flex-col space-y-5 max-w-[500px] mx-auto">
{inputs.map((items, index) => (
<input
key={index}
type="text"
className="border-2 border-black p-1"
placeholder={`${items.placeholder} *`}
required
onChange={(e) => {
items.setState(e.target.value);
}}
/>
))}
<Button
type="submit"
sx={{
border: '2px solid #000000',
color: '#000000',
marginLeft: 'auto',
marginRight: 'auto',
':hover': {
bgcolor: '#ffffff',
color: '#000000',
},
}}
>
Submit
</Button>
</form>
</div>
</>
)
}
CodePudding user response:
You cannot do directly {error}
as error
is not defined as a state
property in the component.
You have to declare it
const [error, setError] = useState('');
and whenever you set error you have to use setError
if (!email.includes(atSymbol)) {
setError('Please enter a valid email');
console.log(error);
return;
}
then in JSX
you can use
{error.length>0 && <p>{error}</p>}
although it will be a common error
for all the inputs
CodePudding user response:
To keep track of the errors for every field you could take this approach.
An object for all the errors with the field name as object key. Update the state with the new error. Now this will not keep track of multiple errors for every field but will eventually pass all of them. Before insertRow
check if there are any errors, if any return.
const [errors, setErrors] = useState({ email: "", password: "" });
const inputs = [
{
email: 'email', // key to get value from errors
placeholder: 'Email',
setState: setEmail
},
...
]
const submitHandler = (e) => {
e.preventDefault(); //Prevents page refresh
setErrors({ email: "", password: "" }); // reset the errors on every submit
//If no @ symobol in email
if (!email.includes(atSymbol)) {
setErrors((prevError) => ({
...prevError,
email: "Please enter a valid email",
}));
// no return needed
}
// ... at the end before navigating check if there are errors
if (errors.email || errors.password) return;
//If all validation passes
insertRow();
navigate('/login');
};
The extra name
prop comes into play when we want to display the errors. After every input we check if there is an error for that field and display it if any.
return (
...
{inputs.map((items, index) => (
<>
<input
key={index}
type="text"
className="border-2 border-black p-1"
placeholder={`${items.placeholder} *`}
required
onChange={(e) => {
items.setState(e.target.value);
}}
/>
{ errors[items.name] && <div>{errors[items.name]}</div>}
</>
))}
...
);
CodePudding user response:
You cannot do directly {error} as error is not defined as a state property in the component.
You have to declare it
const [error, setError] = useState('');
and whenever you set error you have to use setError
if (!email.includes(atSymbol)) {
setError('Please enter a valid email');
console.log(error);
return;
}
then in JSX you can use
{error.length>0 && <p>{error}</p>}
although it will be a common error for all the inputs
for the solution of @kaneki21 i prefer this solution for JSX
{error?.length>0? <p>{error}</p>:null}
in case if error is "undefined" or "null"