Formik 和 yup 验证不适用于所需

Posted

技术标签:

【中文标题】Formik 和 yup 验证不适用于所需【英文标题】:Formik and yup validation not working for required 【发布时间】:2022-01-17 22:23:51 【问题描述】:

我使用 Formik 和 Yup 在 React 中验证我的登录表单。正如您从下面的代码中看到的那样,我检查输入是否为空,然后调用我的后端来检查用户是否存在。对后端的调用有效,因为如果用户不存在,我会在id为“invalidUser”的div中报告错误(我不知道这是否是最正确的解决方案,我想了一种方法将错误插入到formik 的控制形式,但我没有成功)而 required() Yup 不起作用,因为当我不在输入中写任何内容时它不会报告错误。为什么?你有解决方案吗?如果用户存在与否,我的控制解决方案也可以顺利进行还是有更好的解决方案?

index.js:

import React from 'react'
import useNavigate from "react-router-dom";
import Form, Button from 'react-bootstrap';
import LoginWrapper, LoginForm, IconImg, SocialLogin, ErrorMessage from './AccessoElements';
import GoogleLogin from 'react-google-login';
import GoogleButton from 'react-google-button';
import $ from 'jquery';
import FacebookLogin from 'react-facebook-login';
import FaFacebookF from 'react-icons/fa';
import Formik from 'formik';
import * as yup from 'yup';

const responseGoogle = (response) => 
    console.log(response);
    console.log(response.profileObj);


const responseFacebook = (response) => 
    console.log(response);
    console.log(response.profileObj);


const Accesso = () => 
    const schemaLogin = yup.object().shape(
        username: yup.string().required("L'username o l'email è obbligatorio."),
        password: yup.string().required('La password è obbligatoria.'),
    ).test(
        name: "invalidUser",
        test: async (values) => 
            const requestOptions = 
                method: 'POST',
                headers:  'Content-Type': 'application/json' ,
                body: JSON.stringify(
                    "username": values.username,
                    "password": values.password
                )
            ;
            let response = await fetch("http://localhost:8080/api/v1/auth/signin/available", requestOptions)
            if (response.ok) 
                $("#invalidUser").css("display", "none");
                return true;
             else 
                $("#invalidUser").css("display", "flex");
                return this.createError(
                    message: "Email/Username or password invalid.",
                )
            
        
    )

    const navigate = useNavigate();

    return (
        <LoginWrapper>
            <LoginForm>    
                <IconImg src=require('../../../images/avatarUser.png').default  />
                <Formik
                    validationSchema=schemaLogin
                    validateOnChange=false
                    validateOnBlur=false
                    onSubmit=(values) => 
                        const requestOptions = 
                            method: 'POST',
                            headers:  'Content-Type': 'application/json' ,
                            body: JSON.stringify("username": values.username, "password": values.password)
                        ;
                        fetch("http://localhost:8080/api/v1/auth/signin", requestOptions)
                        .then(response => response.json())
                        .then(data => 
                            sessionStorage.setItem("username", data['username']);
                            sessionStorage.setItem("email", data['email']);
                            sessionStorage.setItem("roles", data['roles']);
                            sessionStorage.setItem("isLoggedIn", true);  
                            sessionStorage.setItem("tokenType", data['tokenType']);
                            sessionStorage.setItem("accessToken", data['accessToken']);
                            navigate("/");
                        )
                        .catch(err => console.log(err))
                    
                    initialValues=
                        username: '',
                        password: '',
                    
                    >
                    (
                        handleSubmit,
                        handleChange,
                        values,
                        errors,
                    ) => (
                <Form noValidate onSubmit=handleSubmit>
                    <Form.Group className="position-relative mb-3" controlId="formBasicEmail">
                    <Form.Control
                        type="text"
                        name="username"
                        placeholder="Username"
                        value=values.username
                        onChange=handleChange
                        isInvalid=!!errors.username
                    />
                    <Form.Control.Feedback type="invalid" tooltip>
                        errors.username
                    </Form.Control.Feedback>
                    </Form.Group>
                    <Form.Group className="position-relative mb-3" controlId="formBasicPassword">
                    <Form.Control
                        type="password"
                        name="password"
                        placeholder="Password"
                        value=values.password
                        onChange=handleChange
                        isInvalid=!!errors.password
                    />
                    <Form.Control.Feedback type="invalid" tooltip>
                        errors.password
                    </Form.Control.Feedback>
                    </Form.Group>
                    <Button variant="primary w-100" type="submit">Login</Button>
                </Form>
                )
            </Formik>
            <ErrorMessage id="invalidUser">
                <p>Email/Username o password invalidi.</p>
            </ErrorMessage>
            </LoginForm>
            <div className="divider"><span></span><span>Oppure</span><span></span></div>
            <SocialLogin>
                <div className="container">
                    <FacebookLogin
                        appId="1156207558121098"
                        autoLoad=false
                        fields="name,email,picture"
                        callback=responseFacebook
                        textButton="Accedi con Facebook"
                        icon=<FaFacebookF  style=marginRight: "10px", marginBottom: "3px"></FaFacebookF>
                        cssClass="btnFacebook"
                        language="it_IT"
                    />
                </div>
                <div className="container">
                    <GoogleLogin
                        clientId="459333865802-8u7ted62or2vluagnus58250np433omm.apps.googleusercontent.com"
                        buttonText="Accedi con Google"
                        onSuccess=responseGoogle
                        onFailure=responseGoogle
                        cookiePolicy='single_host_origin'
                        isSignedIn=true
                        language="it_IT"
                        render=renderProps => (
                            <GoogleButton 
                                onClick=renderProps.onClick
                                label='Accedi con Google'
                                style=fontWeight: "700", fontFamily: "Helvetica, sans-serif", WebkitFontSmoothing: "antialiased", justifyContent: "center", minWidth: "240px"
                            />
                        )
                    />
                </div>
            </SocialLogin>
        </LoginWrapper>
    )


export default Accesso

AccessoElements.js:

import styled from 'styled-components';

export const LoginWrapper = styled.div`
    align-items: center;
    min-height: 100vh;
    background: #0c0c0c;
`

export const LoginForm = styled.div`
    width: 25%;
    margin: 0 auto;
    margin-bottom: 50px;
    padding-top: 140px;
    text-align: center;

    @media screen and (max-width: 968px) 
        width: 45%;
    

    @media screen and (max-width: 768px) 
        width: 55%;
    
`

export const SocialLogin = styled.div`
    display: flex;
    width: 30%;
    margin: 0 auto;
    justify-content: center;

    @media screen and (max-width: 968px) 
        width: 55%;
    

    @media screen and (max-width: 600px) 
        flex-wrap: wrap;
    
`

export const IconImg = styled.img`
    width: 70px;
    height: 70px;
    margin-bottom: 2rem;
`

export const ErrorMessage = styled.div`
    display: none;
    color: #dc3545;
    margin-top: 1rem;

    p 
        margin-bottom: 0rem;
    
`

【问题讨论】:

【参考方案1】:

在使用 React 时应避免直接访问 DOM,因此应将 invalidUser 信息保存在状态中,然后根据该状态显示/隐藏 ErrorMessage

您也可以在实际提交之前直接对用户进行验证。

因此您可以执行以下操作:

import React from 'react'
import useNavigate from "react-router-dom";
import Form, Button from 'react-bootstrap';
import LoginWrapper, LoginForm, IconImg, SocialLogin, ErrorMessage from './AccessoElements';
import GoogleLogin from 'react-google-login';
import GoogleButton from 'react-google-button';
import $ from 'jquery';
import FacebookLogin from 'react-facebook-login';
import FaFacebookF from 'react-icons/fa';
import Formik from 'formik';
import * as yup from 'yup';

const responseGoogle = (response) => 
    console.log(response);
    console.log(response.profileObj);


const responseFacebook = (response) => 
    console.log(response);
    console.log(response.profileObj);


const Accesso = () => 
    const [invalidUser, setInvalidUser] = React.useState(false)
    const testUser = async (values) => 
            const requestOptions = 
                method: 'POST',
                headers:  'Content-Type': 'application/json' ,
                body: JSON.stringify(
                    "username": values.username,
                    "password": values.password
                )
            ;
            let response = await fetch("http://localhost:8080/api/v1/auth/signin/available", requestOptions)
            setInvalidUser(!response.ok)
            return response.ok
        
    const schemaLogin = yup.object().shape(
        username: yup.string().required("L'username o l'email è obbligatorio."),
        password: yup.string().required('La password è obbligatoria.'),
    )

    const navigate = useNavigate();

    return (
        <LoginWrapper>
            <LoginForm>    
                <IconImg src=require('../../../images/avatarUser.png').default  />
                <Formik
                    validationSchema=schemaLogin
                    validateOnChange=false
                    validateOnBlur=false
                    onSubmit=async (values) => 
                        const isValid = await testUser(values)
                        if (isValid) 

                          const requestOptions = 
                              method: 'POST',
                              headers:  'Content-Type': 'application/json' ,
                              body: JSON.stringify("username": values.username, "password": values.password)
                          ;
                          fetch("http://localhost:8080/api/v1/auth/signin", requestOptions)
                          .then(response => response.json())
                          .then(data => 
                              sessionStorage.setItem("username", data['username']);
                              sessionStorage.setItem("email", data['email']);
                              sessionStorage.setItem("roles", data['roles']);
                              sessionStorage.setItem("isLoggedIn", true);  
                              sessionStorage.setItem("tokenType", data['tokenType']);
                              sessionStorage.setItem("accessToken", data['accessToken']);
                              navigate("/");
                          )
                          .catch(err => console.log(err))
                        
                    
                    initialValues=
                        username: '',
                        password: '',
                    
                    >
                    (
                        handleSubmit,
                        handleChange,
                        values,
                        errors,
                    ) => (
                <Form noValidate onSubmit=handleSubmit>
                    <Form.Group className="position-relative mb-3" controlId="formBasicEmail">
                    <Form.Control
                        type="text"
                        name="username"
                        placeholder="Username"
                        value=values.username
                        onChange=handleChange
                        isInvalid=!!errors.username
                    />
                    <Form.Control.Feedback type="invalid" tooltip>
                        errors.username
                    </Form.Control.Feedback>
                    </Form.Group>
                    <Form.Group className="position-relative mb-3" controlId="formBasicPassword">
                    <Form.Control
                        type="password"
                        name="password"
                        placeholder="Password"
                        value=values.password
                        onChange=handleChange
                        isInvalid=!!errors.password
                    />
                    <Form.Control.Feedback type="invalid" tooltip>
                        errors.password
                    </Form.Control.Feedback>
                    </Form.Group>
                    <Button variant="primary w-100" type="submit">Login</Button>
                </Form>
                )
            </Formik>
            invalidUser && <ErrorMessage id="invalidUser">
                <p>Email/Username o password invalidi.</p>
            </ErrorMessage>
            </LoginForm>
            <div className="divider"><span></span><span>Oppure</span><span></span></div>
            <SocialLogin>
                <div className="container">
                    <FacebookLogin
                        appId="1156207558121098"
                        autoLoad=false
                        fields="name,email,picture"
                        callback=responseFacebook
                        textButton="Accedi con Facebook"
                        icon=<FaFacebookF  style=marginRight: "10px", marginBottom: "3px"></FaFacebookF>
                        cssClass="btnFacebook"
                        language="it_IT"
                    />
                </div>
                <div className="container">
                    <GoogleLogin
                        clientId="459333865802-8u7ted62or2vluagnus58250np433omm.apps.googleusercontent.com"
                        buttonText="Accedi con Google"
                        onSuccess=responseGoogle
                        onFailure=responseGoogle
                        cookiePolicy='single_host_origin'
                        isSignedIn=true
                        language="it_IT"
                        render=renderProps => (
                            <GoogleButton 
                                onClick=renderProps.onClick
                                label='Accedi con Google'
                                style=fontWeight: "700", fontFamily: "Helvetica, sans-serif", WebkitFontSmoothing: "antialiased", justifyContent: "center", minWidth: "240px"
                            />
                        )
                    />
                </div>
            </SocialLogin>
        </LoginWrapper>
    )


export default Accesso

当然,将上述代码视为提示,以便开始为自己找到最佳解决方案。

【讨论】:

非常感谢!

以上是关于Formik 和 yup 验证不适用于所需的主要内容,如果未能解决你的问题,请参考以下文章

使用 Formik 和 Yup 和 React-select 进行验证

Formik + Yup:如何在提交前立即验证表单?

Yup.mixed().test() 似乎破坏了 Formik 表单验证

Formik Yup `.test()` 自定义验证无法正常工作

Null 被设置为 Formik 中空字符串的初始值,而反应原生中的 Yup 表单验证

使用 Yup 和 Formik 自动修剪空白