skip to Main Content

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


  1. Chosen as BEST ANSWER

    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:

    const LoginForm = () => {
        if (adminExists) return someComponent;
        else return someOtherComponent
    }
    
    return (
        <LoginForm/>
    )
    

    I now do:

    if (adminExists) return (
        <div ...props>
            ... the login form and the rest
        </div>
    )
    else return (
        <div ...props>
            ...the signup form and the rest
        </div>
    )
    

    This stopped the entire page re-rendering on every value entered into an input box.


  2. 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 the adminExists state, which might re-mount your components.

    Just pass an empty array as dependency to useEffect and that will solve your problem:

    useEffect(() => {
        ... request from server
        setAdminExists(true/false)
    }, [])
    

    Here are some tips unrelated to your main question:

    • Consider using onSubmit or onChange events rather than onKeyUp, It’s not for such a case.

    • There is no need to define submitEnabled state here, just define a derived state based errors state.

    const submitEnabled = Boolean(errors);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search