尽管 useEffect 反应状态落后一步

时间:2021-01-10 18:44:03

标签: javascript reactjs typescript ionic-framework

我有一个用于用户登录的 React 表单,除了设置成功或不成功的消息外,一切正常。登录后,根据用户是否注册,我将 useState variable [res, setRes] 的值设置为成功或成功。问题是,即使用户已注册且用户名和密码正确,我也至少收到一次“无效凭据”消息。来自同一用户的后续调用会导致显示正确的消息。搜了一下,发现state落后一步,解决办法是用useEffect,不过我已经在用了。谁能帮我弄清楚问题是什么?代码如下

export const Login = () => {
    const email = useField('string')
    const password = useField('password')
    const [cred, setCred] = useState({})
    const send = (e:any) => {
        e.preventDefault()
        setCred({'email':email.value, 'password':password.value}) 
            showToast()
    }
    const [toastIsShown, setToastIsShown] = useState(false);

    const showToast = () => {
        setToastIsShown(true);
    }
    const [res,setRes] = useState('')

    const hook = () => {
        axios
        .post('http://localhost:5000/auth', cred)
        .then(response => { 
            console.log('response is ',response.data)
            setRes('Login Successful')
    })
        .catch(err => {
            console.log("error is ",err.response)
            setRes('Invalid username or password')
        })
    }
    useEffect(hook,[cred])
    return (
        <>
            <form onSubmit = {send}>
                <IonText>Enter Name</IonText>
                <br />
                <input {...email} />
                <br />
                <IonText>Enter Password</IonText>
                <br />
                <input {...password} />
                <br />
                <button>Send</button>
            </form>
            <IonToast
                        isOpen={toastIsShown}
                        onDidDismiss={() => setToastIsShown(false)}
                        message={res}
                        duration={3000}
            />
        </>
    )
}

我正在使用 Ionic,这就是为什么你会在那里看到 Toast。此外,语言是 Typescript。 谢谢

1 个答案:

答案 0 :(得分:2)

useEffect 钩子在组件挂载时总是被调用,之后每次其依赖数组中的值发生变化时都会调用。由于空对象可能不是有效的登录,因此在安装组件时您总是会尝试失败。您可以进行一些简单的验证,例如:

cred.email && cred.password && axios.post('http://localhost:5000/auth', cred)...

然而,问题的根源在于您滥用了 useEffect。登录尝试(通常,在您的情况下)是一次性事件,而不是由于之前的重大操作而产生的副作用。这种情况下的副作用发生在登录尝试之后,当您触发包含有关结果的通知的 Toast 时:

export const Login = () => {
    const email = useField('string');
    const password = useField('password');
    
    const [res, setRes] = useState('');
    const [toastIsShown, setToastIsShown] = useState(false);

    const send = (e:any) => {
        e.preventDefault();

        const cred = {
            email: email.value,
            password: password.value
        };

        axios
        .post('http://localhost:5000/auth', cred)
        .then(response => { 
            console.log('response is ',response.data)
            setRes('Login Successful');
        })
        .catch(err => {
            console.log("error is ",err.response)
            setRes('Invalid username or password');
        });
    };

    useEffect(() => {
        res && setToastIsShown(true);
    }, [res]);
    
    return (
        ...
    )
}

这只是为了演示更合理地使用 useEffect。实际上,我可能甚至不会在这里使用,而是在设置 setToastIsShown 后从 send 内部调用 res。当您有两个相关的数据片段由多个不相关的方法更新时,useEffect 真的会派上用场。