Почему кнопка работает только после второго нажатия? (reactjs) - PullRequest
1 голос
/ 04 мая 2020

У меня есть кнопка входа в систему, и если каждое поле ввода заполнено правильно (если оно действительно) в первый раз, то оно работает нормально, но если нет, мне нужно щелкнуть 3 раза, чтобы войти (в первый раз я получаю сообщение об ошибке этот ввод недействителен, второй щелчок очищает эту ошибку (и), а третий выполняет вход в систему).

import React, { useRef, useContext, useState } from 'react'
import { AppContext } from '../context/AppContext'
import { useHistory } from 'react-router-dom'

function Login() {

    const { setUser } = useContext(AppContext)

    const history = useHistory()

    const usernameRef = useRef<HTMLInputElement>(null)
    const emailRef = useRef<HTMLInputElement>(null)
    const passwordRef = useRef<HTMLInputElement>(null)

    const [valid, setValid] = useState({ usernameErr: '', emailErr: '', passwordErr: '' })

    const handleValidation = () => {
        let usernameMsg = ''
        let passwordMsg = ''
        let emailMsg = ''

        if (usernameRef.current?.value === "")
            usernameMsg = "Username is not valid"
        if (!emailRef.current?.value.includes('@') && !emailRef.current?.value.includes('.'))
            emailMsg = "Email is not valid"
        if (passwordRef.current?.value !== undefined && passwordRef.current.value.length < 5)
            passwordMsg = "Password must contain at least 5 characters"

        setValid({
            usernameErr: usernameMsg,
            emailErr: emailMsg,
            passwordErr: passwordMsg
        })
    }

    const handleLogin = () => {
        handleValidation()
        console.log("validation: ", valid)
        if (usernameRef.current?.value && emailRef.current?.value && passwordRef.current?.value) {
            if (valid.usernameErr === '' && valid.emailErr === '' && valid.passwordErr === '') {
                setUser(
                    {
                        username: usernameRef.current.value,
                        password: passwordRef.current.value,
                        email: emailRef.current.value,
                        isLogin: true
                    })
                history.goBack()
            }
        }
    }

    return (
        <div className="login-outter page">
            <div className="login-inner">
                <p className="login-title">Log in here</p>
                <input type="text" placeholder="Username" ref={usernameRef} className="about-input"/>
                <p className="valid">
                    {valid.usernameErr}
                </p>
                <input type="text" placeholder="Email" ref={emailRef} className="about-input"/>
                <p className="valid">
                    {valid.emailErr}
                </p>
                <input type="password" placeholder="Password" ref={passwordRef} className="about-input"/>
                <p className="valid">
                    {valid.passwordErr}
                </p>
                <button className="login-btn" onClick={handleLogin}>Login</button>
            </div>
        </div>
    )
}

export default Login

Вот песочница кода: https://codesandbox.io/s/flamboyant-torvalds-tevby?file= / src / components / pages / Login.tsx

Я предполагаю, что это из-за useState, но не знаю, что именно, почему и как это исправить.

Ответы [ 2 ]

1 голос
/ 05 мая 2020

handleLogin() получает значение состояния valid из цикла рендеринга, в котором функция была выполнена (была нажата кнопка).

Это означает, что когда вы вызываете handleValidation() внутри handleLogin(), это не повлияет состояние valid в том же контексте немедленно, но повлияет на состояние в следующем цикле рендеринга, где будет создана новая функция handleLogin (и не будет выполняться, пока не будет нажата кнопка) .

Решение состоит в том, чтобы функция валидатора, например handleValidation(), возвращала результат валидации напрямую, тогда вы можете установить состояние сообщений об ошибках внутри handleLogin()

1 голос
/ 04 мая 2020

Это связано с тем, что обновления хуков состояния React асинхронны, как я упоминал в комментариях. Вот пример того, как вы можете исправить это, слегка изменив текущий код: (Не самое элегантное решение, но его самые маленькие изменения в вашем коде, и он работает)

    const handleValidation = () => {
        let usernameMsg = ''
        let passwordMsg = ''
        let emailMsg = ''

        if (usernameRef.current?.value === "")
            usernameMsg = "Username is not valid"
        if (!emailRef.current?.value.includes('@') && !emailRef.current?.value.includes('.'))
            emailMsg = "Email is not valid"
        if (passwordRef.current?.value !== undefined && passwordRef.current.value.length < 5)
            passwordMsg = "Password must contain at least 5 characters"

        setValid({
            usernameErr: usernameMsg,
            emailErr: emailMsg,
            passwordErr: passwordMsg
        })
        return (usernameMsg === '' && passwordMsg === '' && emailMsg === '')
    }

    const handleLogin = () => {
        if (handleValidation() && usernameRef.current?.value && emailRef.current?.value && passwordRef.current?.value) {
                setUser(
                    {
                        username: usernameRef.current.value,
                        password: passwordRef.current.value,
                        email: emailRef.current.value,
                        isLogin: true
                    })
                history.goBack()
        }
    }

Вот это по вашей ссылке : https://codesandbox.io/s/strange-lamport-olcy0?file= / src / components / pages / Login.tsx

...