Okay so breaking down my code into chunks I have the following in HTML:
<input
type="text"
name="email"
id="email"
autoComplete="email"
onChange={(e) => {validateField(e.target)}}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
<FormFieldError
Field='email'
FormFieldErrors={formFieldErrors}
/>
The validateField
function looks like this:
const validateField = (field) => {
// console.log('validatefield', field.id, field.value)
switch(field.id) {
case 'email':
const pattern = /[a-zA-Z0-9] [\.]?([a-zA-Z0-9] )?[\@][a-z]{3,9}[\.][a-z]{2,5}/g
const result = pattern.test(field.value)
if (result !== true) {
setFormFieldErrors({...formFieldErrors, email: 'Please enter a valid email address'})
console.log('Please enter a valid email address')
} else {
setFormFieldErrors({...formFieldErrors, email: null})
}
break
}
}
The FormFieldError
function looks like this:
function FormFieldError({ Field, FormFieldErrors }) {
let message = FormFieldErrors[Field] ? FormFieldErrors[Field] : null
return (
<>
<div className="text-red-500 text-xs text-bold">{(message)}</div>
</>
)
}
Now, when in the onSubmit of the form I got to the following function:
const submitNewRegistration = async (event) => {
event.preventDefault()
Array.prototype.forEach.call(event.target.elements, (element) => {
validateField(element);
})
let errors = Object.keys(formFieldErrors).some(key => key !== null)
console.log(errors)
}
I am declaring formFieldErrors in my state like this:
const [formFieldErrors, setFormFieldErrors] = useState({})
When I am changing a field the onChange function works perfectly for each input, and if the input is wrong then the message below the input shows up thanks to the formFieldErrors displays the message underneath the input. However, when I call validateInput
from my submitNewRegistration
function, setFormFieldErrors
doesnt seem to be called. In fact, even if I put setFormFieldErrors
in to submitNewRegistration
it doesnt seem to change the state of formFieldErrors
. So when I am trying to validate the form just before submitting it I do not get any errors.
Can anyone explain to me why its working with the onChange method and now when I call it from submitNewRegistration
??
CodePudding user response:
It's not that setFormFieldErrors
isn't called, the issue is you are trying to rely on the state value before the state has been updated. When you call setState
the state doesn't update immediately. This is because setState
is asynchronous, essentially a promise which resolves upon the next render of your component.
So to solve this, you need to strip your validation logic outside of the state update. In other words:
const validateField = (field) => {
// console.log('validatefield', field.id, field.value)
switch(field.id) {
case 'email':
const pattern = /[a-zA-Z0-9] [\.]?([a-zA-Z0-9] )?[\@][a-z]{3,9}[\.][a-z]{2,5}/g
const result = pattern.test(field.value)
if (result !== true) {
return {email: 'Please enter a valid email address'}
console.log('Please enter a valid email address')
} else {
return {email: null}
}
break
}
Then in your html:
<input
type="text"
name="email"
id="email"
autoComplete="email"
onChange={(e) => {
setFormFieldErrors({...formFieldErrors, ...validateField(e.target)})
}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
/>
<FormFieldError
Field='email'
FormFieldErrors={formFieldErrors}
/>
And finally in the submit:
const submitNewRegistration = async (event) => {
event.preventDefault()
let formErrors = formFieldErrors;;
Array.prototype.forEach.call(event.target.elements, (element) => {
formErrors = {...formErrors, ...validateField(element)};
})
setFormFieldErrors(formErrors)
let errors = Object.keys(formErrors).some(key => key !== null)
console.log(errors)
}