I have developed a React+NextJS application. The login page on load uses a useEffect
hook to ask my server if the admin account exists. This in turn sets a hook, which affects what content is loaded.
const [adminExists, setAdminExists] = useState(false); // <- assume account does not exist
const [submitEnabled, setSubmitEnabled] = useState(false);
const [errors, setErrors] = useState("");
useEffect(() => {
... request from server
setAdminExists(true/false)
})
Then the return function of the page:
const LoginForm = () => {
if (adminExists) {
return (...loginForm)
} else {
return (
<div ...props>
<h1>title</h1>
<Form ...props>
<div ...props>
<Input ...props/> // <- username
</div>
<div ...props>
<Input ...props/> // <- password
</div>
<div ...props>
<Input ...props/> // <- password confirm
</div>
<div ...props>
<Button ...props isDisabled={!submitEnabled}/> // <- submit button
</div>
<div ...props>
{errors}
</div>
</Form>
</div>
)
}
}
return (
<LoginForm/>
)
The Input
boxes have a key up listener as follows:
onKeyUp={validatePasswords}
And validatePasswords
:
const validatePasswords = (e) => {
// extract form data
const password = form.get('password')
const passwordConfirm = form.get('passwordConfirm')
setSubmitEnabled(password.length > 0 && passwordConfirm > 0 && password === passwordConfirm)
if (!password.startsWith(passwordConfirm)) {
setErrors("Passwords must match")
} else {
setErrors("") // <- clear errors
}
}
The problem:
- When I type in the input box, if the
startsWith
check is true, nothing happens (as expected) - When the passwords match exactly, the input boxes clear and the submit button doesn’t actually get enabled
- When the confirm password input doesn’t match the
startsWith
check, the input boxes clear. The error appears for a split second and disappears.
I believe the parent component is re-rendering when the hook is changed or am I wrong?
How do I get this to work? I am not very experienced with React or NextJS so help is much appreciated.
2
Answers
Ok so I was playing around and it seems the issue is in the
LoginForm
const. It just doesn't like the fact its being returned from a call. I really don't understand why, perhaps someone with more understanding could explain?However, I fixed the input box issue by moving the form into the main return of the component.
So, instead of:
I now do:
This stopped the entire page re-rendering on every value entered into an input box.
When the passwords match, you update a state, and because you didn’t pass a dependency array to
useEffect
, it re-runs its callback on every re-render. This potentially changes theadminExists
state, which might re-mount your components.Just pass an empty array as dependency to
useEffect
and that will solve your problem:Here are some tips unrelated to your main question:
Consider using
onSubmit
oronChange
events rather thanonKeyUp
, It’s not for such a case.There is no need to define
submitEnabled
state here, just define a derived state basederrors
state.