Formik 和 yup 验证不适用于所需的
Formik and yup validation not working for required
我使用 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} alt="icon" />
<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;
}
`
你应该避免在使用 React 时直接访问 DOM,所以你应该将 invalidUser
信息保存在一个状态中,然后 show/hide 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} alt="icon" />
<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 在 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} alt="icon" />
<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;
}
`
你应该避免在使用 React 时直接访问 DOM,所以你应该将 invalidUser
信息保存在一个状态中,然后 show/hide 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} alt="icon" />
<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
当然,将上面的代码当作一个提示来考虑,以便开始为自己寻找最佳解决方案。