I am writting a simple form for registring an account. The user should input his name, e-mail, password and a confirmation of his password.
My validates the password inside the onChange event in the input html. Here’s my component:
import React, { useState } from 'react'
import Input from './Input'
const Register = () => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [pswdConfirm, setPswdConfirm] = useState('')
function validatePassword() {
setPasswordError('')
setPswdConfirmError('')
if (password.length < 6) {
setPasswordError('Password must be at least 7 characters longs')
return false
}
if (password !== pswdConfirm) {
setPswdConfirmError('Passwords are not identical')
return false
}
return true
}
const onSubmit = async (e) => {
e.preventDefault();
if (!validatePassword()) { // here it works
return;
}
}
return (
<div className='d-flex justify-content-center align-items-center fullscreen'>
<form className='h-50 d-block central-card container'>
<Input
label='Fullname'
type='text'
onChange={(e) => setName(e.target.value)}
/>
<Input
label='E-mail'
type='text'
onChange={(e) => setEmail(e.target.value)}
/>
<Input
label='Password'
type='password'
error={passwordError}
onChange={(e) => {
setPassword(e.target.value)
validatePassword() // here it doesnt
console.log('password', password)
}}
/>
<Input
label='Confirm password'
type='password'
error={pswdConfirmError}
onChange={(e) => {
setPswdConfirm(e.target.value)
validatePassword() // and neither here
console.log('password confirmation', pswdConfirm)
}}
/>
<button className="w-100 row align-items-center" onClick={(e) => onSubmit(e)}>
<span className="text-center">Sign-up</span>
</button>
</form>
</div>
)
}
export default Register
However, my setter from react hooks (‘setPassword’ and ‘setPswdConfirm’) do not seem to update my component state asynchronously which results in my ‘onChange’ valiation constantly failing, as the actual value in my component is always lagging one char behind the actual input. For example, when I type ‘1’, my function will try to validate ”, when I type ’12’ it will instead execute with ‘1’, when I input ‘123’, ’12’ and so on.
I was expecting my react-hook setter to syncrhously update my state so I could promptly valite the password. Am I missing something or not using this library appropriately?
I could instead run my valiation on e.target.value, however I’d like to understand react-hooks better and try for a more elegant solution before trying workarounds.Should I instead use ‘useEffect’?
2
Answers
I think you are better off using useEffect to synchronise your validation.
From react docs:
useEffect version:
You have to use the useEffect hook in order for the validation to be real time, the useEffect runs everytime the dependency changes (the dependency is the second argument which is an array), so I will modify your code like this and remove the validatePassword fn.
Explanation:
Every time the password or pswdConfirm changes the useEffect will run whatever is inside the function because they are included in the dependency list [password, pswdConfirm]. Aditionally I added the reset errors in an else statement, so you don’t call it everytime the input changes.
While your validate password returns a boolean, this is not actually needed, because you can validate yourself using the passwordError and pswdConfirmError values in the onSubmit function like in the code I shared
The validatePassword will work but I think is redundant, so what you want to do in your effect is to modify the passwordError and pswdConfirmError. Please modify the code accordingly because I used regular inputs instead of the Input component you shared and remember to add the name prop to the inputs to match with the labels and improve a11y.
Here’s the code sandbox in case you want to look how it looks:
https://codesandbox.io/s/amazing-stallman-yd6g9h?file=/src/App.js