如何正确使用 Formik 的 setError 方法? (反应库)
How to properly use Formik's setError method? (React library)
我正在使用 React 与后端通信。现在正在尝试正确实施 Formik(表单库)。
主要问题:
如何正确使用 Formik 的 setError 方法?
客户端验证错误正确显示,但现在我正在尝试 set/show 后端验证错误,这些错误返回状态代码为 400 的响应。
Link 关于我尝试使用的方法的文档
我在下面代码中名为 handle400Error 的方法中使用了这个方法。
我的 React(和 Formik)代码:
import React, { Component } from "react";
import axios from "axios";
import { Formik } from "formik";
import * as Yup from "yup";
import styled from "styled-components";
import FormError from "../formError";
const Label = styled.label``;
class LoginForm extends Component {
initialValues = {
password: "",
username: ""
};
getErrorsFromValidationError = validationError => {
const FIRST_ERROR = 0;
return validationError.inner.reduce((errors, error) => {
return {
...errors,
[error.path]: error.errors[FIRST_ERROR]
};
}, {});
};
getValidationSchema = values => {
return Yup.object().shape({
password: Yup.string()
.min(6, "Password must be at least 6 characters long")
.required("Password is required!"),
username: Yup.string()
.min(5, "Username must be at least 5 characters long")
.max(40, "Username can not be longer than 40 characters")
.required("Username is required")
});
};
handleSubmit = async (values, { setErrors }) => {
console.log("handleSubmit");
try {
const response = await axios.post(
"http://127.0.0.1:8000/rest-auth/login/",
values
);
const loginToken = response.data["key"];
this.handleLoginSuccess(loginToken);
} catch (exception) {
// Expected: 400 status code
if (exception.response && exception.response.status === 400) {
// Display server validation errors
this.handle400Error(exception.response.data, setErrors);
}
console.log("exception", exception);
console.log("exception.response", exception.response);
}
};
handle400Error = (backendErrors, setErrors) => {
let errors = {};
for (let key in backendErrors) {
errors[key] = backendErrors[key][0]; // for now only take the first error of the array
}
console.log("errors object", errors);
setErrors({ errors });
};
handleUnexpectedError = () => {};
handleLoginSuccess = loginToken => {
console.log("handleLoginSuccess");
this.props.setGreeneryAppState({
loginToken: loginToken
});
this.props.history.replace(`/${this.props.locale}/`);
};
validate = values => {
const validationSchema = this.getValidationSchema(values);
try {
validationSchema.validateSync(values, { abortEarly: false });
return {};
} catch (error) {
return this.getErrorsFromValidationError(error);
}
};
render() {
return (
<React.Fragment>
<h1>Login</h1>
<Formik
initialValues={this.initialValues}
validate={this.validate}
validationSchema={this.validationSchema}
onSubmit={this.handleSubmit}
render={({
errors,
touched,
values,
handleBlur,
handleChange,
handleSubmit
}) => (
<form onSubmit={handleSubmit}>
{errors.non_field_errors && (
<formError>{errors.non_field_errors}</formError>
)}
<Label>Username</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
type="text"
name="username"
placeholder="Enter username"
/>
{touched.username &&
errors.username && <FormError>{errors.username}</FormError>}
<Label>Password</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
type="password"
name="password"
placeholder="Enter password"
/>
{touched.password &&
errors.password && <FormError>{errors.password}</FormError>}
<button type="submit">Log in</button>
</form>
)}
/>
</React.Fragment>
);
}
我刚刚解决了我自己的问题。
我需要使用:
setErrors( errors )
而不是:
setErrors({ errors })
Formik 作者在这里...
setError
原为 deprecated in v0.8.0 并重命名为 setStatus
。您可以在 handleSubmit
函数中使用 setErrors(errors)
或 setStatus(whateverYouWant)
来获得您想要的行为,如下所示:
handleSubmit = async (values, { setErrors, resetForm }) => {
try {
// attempt API call
} catch(e) {
setErrors(transformMyApiErrors(e))
// or setStatus(transformMyApiErrors(e))
}
}
使用 setStatus
与 setErrors
有什么区别?
如果您使用 setErrors
,您的错误将被 Formik 的下一个 validate
或 validationSchema
调用消除,这可以由用户键入(更改事件)或模糊触发输入(模糊事件)。注意:这是假设您没有手动将 validateOnChange
和 validateOnBlur
属性设置为 false
(默认情况下它们是 true
)。
恕我直言 setStatus
在这里实际上是理想的,因为它将错误消息放在 Formik 状态的单独部分。然后,您可以像这样决定如何/何时向最终用户显示此消息。
// status can be whatever you want
{!!status && <FormError>{status}</FormError>}
// or mix it up, maybe transform status to mimic errors shape and then ...
{touched.email && (!!errors.email && <FormError>{errors.email}</FormError>) || (!!status && <FormError>{status.email}</FormError>) }
请注意 status
的存在或值对阻止下一次表单提交没有影响。 Formik 仅中止 submission process if validation fails.
处理这种情况的另一种方法是为您的 api
错误分配一个特定的键,并使用 setStatus
来显示状态消息。
__handleSubmit = (values, {setStatus, setErrors}) => {
return this.props.onSubmit(values)
.then(() => {
setStatus("User was updated successfully.");
})
.catch((err) => {
setErrors({api: _.get(err, ["message"])});
});
}
然后任何验证错误都会出现在字段旁边,任何 api 错误可能会出现在底部:
<Formik
validationSchema={LoginSchema}
initialValues={{login: ""}}
onSubmit={this.__handleSubmit}
>
{({isSubmitting, status, errors, values, setFieldValue}) => (
<Form className={classNames("form")}>
<FormGroup>
<InputGroup>
<InputGroup.Text>
<FontAwesomeIcon icon={faUser} fixedWidth />
</InputGroup.Text>
<Field
name="login"
type={"text"}
placeholder="Login"
className="form-control"
/>
</InputGroup>
<ErrorMessage name="login" />
</FormGroup>
<Button type="submit" variant="primary" disabled={isSubmitting}>
Submit
</Button>
{errors && _.has(errors, ["api"]) && <div className="text-danger">{_.get(errors, ["api"])}</div>}
{status && <div className="text-success">{status}</div>}
</Form>
)}
</Formik>
不要忘记模式...
const LoginSchema = Yup.object().shape({
login: Yup.string()
.min(4, 'Too Short!')
.max(70, 'Too Long!')
.required('Login is required'),
});
api
错误消息将显示到 Formik 的下一次验证调用(即用户正在修复某些东西)。但是 status
消息将一直存在,直到您清除它(使用计时器或淡入淡出)。
<Formik
validationSchema={schema}
initialValues={{ email: '', pswrd: '' }}
onSubmit={(values, actions) => {
// initialise error status <---- 1
actions.setStatus(undefined);
setTimeout(() => {
// setting error status <---- 2
actions.setStatus({
email: 'This is email already exists.',
pswrd: 'This is password is incorrect',
});
}, 500);
}}
// destructuring status <---- 3
render={({ handleSubmit, handleChange, handleBlur, values, errors, status }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
name="email"
value={values['email']}
onChange={handleChange}
onBlur={handleBlur}
/>
<input
type="text"
name="pswrd"
value={values['pswrd']}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
// using error status <---- 4
{status && status.email ? (
<div>API Error: {status.email}</div>
) : (
errors.email && <div>Validation Error: {errors.email}</div>
)}
{status && status.pswrd ? (
<div>API Error: {status.pswrd}</div>
) : (
errors.pswrd && <div>Validation Error: {errors.pswrd}</div>
)}
</form>
)}
/>
const formik = useFormik({
initialValues:{
email:"",password:"",username:""
},
validationSchema:validation_schema,
onSubmit:(values) => {
const {email,password,username} = values
// ......
}
});
formik.setErrors({email:"Is already taken"}) // error message for email field
我正在使用 React 与后端通信。现在正在尝试正确实施 Formik(表单库)。
主要问题: 如何正确使用 Formik 的 setError 方法?
客户端验证错误正确显示,但现在我正在尝试 set/show 后端验证错误,这些错误返回状态代码为 400 的响应。
Link 关于我尝试使用的方法的文档
我在下面代码中名为 handle400Error 的方法中使用了这个方法。
我的 React(和 Formik)代码:
import React, { Component } from "react";
import axios from "axios";
import { Formik } from "formik";
import * as Yup from "yup";
import styled from "styled-components";
import FormError from "../formError";
const Label = styled.label``;
class LoginForm extends Component {
initialValues = {
password: "",
username: ""
};
getErrorsFromValidationError = validationError => {
const FIRST_ERROR = 0;
return validationError.inner.reduce((errors, error) => {
return {
...errors,
[error.path]: error.errors[FIRST_ERROR]
};
}, {});
};
getValidationSchema = values => {
return Yup.object().shape({
password: Yup.string()
.min(6, "Password must be at least 6 characters long")
.required("Password is required!"),
username: Yup.string()
.min(5, "Username must be at least 5 characters long")
.max(40, "Username can not be longer than 40 characters")
.required("Username is required")
});
};
handleSubmit = async (values, { setErrors }) => {
console.log("handleSubmit");
try {
const response = await axios.post(
"http://127.0.0.1:8000/rest-auth/login/",
values
);
const loginToken = response.data["key"];
this.handleLoginSuccess(loginToken);
} catch (exception) {
// Expected: 400 status code
if (exception.response && exception.response.status === 400) {
// Display server validation errors
this.handle400Error(exception.response.data, setErrors);
}
console.log("exception", exception);
console.log("exception.response", exception.response);
}
};
handle400Error = (backendErrors, setErrors) => {
let errors = {};
for (let key in backendErrors) {
errors[key] = backendErrors[key][0]; // for now only take the first error of the array
}
console.log("errors object", errors);
setErrors({ errors });
};
handleUnexpectedError = () => {};
handleLoginSuccess = loginToken => {
console.log("handleLoginSuccess");
this.props.setGreeneryAppState({
loginToken: loginToken
});
this.props.history.replace(`/${this.props.locale}/`);
};
validate = values => {
const validationSchema = this.getValidationSchema(values);
try {
validationSchema.validateSync(values, { abortEarly: false });
return {};
} catch (error) {
return this.getErrorsFromValidationError(error);
}
};
render() {
return (
<React.Fragment>
<h1>Login</h1>
<Formik
initialValues={this.initialValues}
validate={this.validate}
validationSchema={this.validationSchema}
onSubmit={this.handleSubmit}
render={({
errors,
touched,
values,
handleBlur,
handleChange,
handleSubmit
}) => (
<form onSubmit={handleSubmit}>
{errors.non_field_errors && (
<formError>{errors.non_field_errors}</formError>
)}
<Label>Username</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
type="text"
name="username"
placeholder="Enter username"
/>
{touched.username &&
errors.username && <FormError>{errors.username}</FormError>}
<Label>Password</Label>
<input
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
type="password"
name="password"
placeholder="Enter password"
/>
{touched.password &&
errors.password && <FormError>{errors.password}</FormError>}
<button type="submit">Log in</button>
</form>
)}
/>
</React.Fragment>
);
}
我刚刚解决了我自己的问题。
我需要使用:
setErrors( errors )
而不是:
setErrors({ errors })
Formik 作者在这里...
setError
原为 deprecated in v0.8.0 并重命名为 setStatus
。您可以在 handleSubmit
函数中使用 setErrors(errors)
或 setStatus(whateverYouWant)
来获得您想要的行为,如下所示:
handleSubmit = async (values, { setErrors, resetForm }) => {
try {
// attempt API call
} catch(e) {
setErrors(transformMyApiErrors(e))
// or setStatus(transformMyApiErrors(e))
}
}
使用 setStatus
与 setErrors
有什么区别?
如果您使用 setErrors
,您的错误将被 Formik 的下一个 validate
或 validationSchema
调用消除,这可以由用户键入(更改事件)或模糊触发输入(模糊事件)。注意:这是假设您没有手动将 validateOnChange
和 validateOnBlur
属性设置为 false
(默认情况下它们是 true
)。
恕我直言 setStatus
在这里实际上是理想的,因为它将错误消息放在 Formik 状态的单独部分。然后,您可以像这样决定如何/何时向最终用户显示此消息。
// status can be whatever you want
{!!status && <FormError>{status}</FormError>}
// or mix it up, maybe transform status to mimic errors shape and then ...
{touched.email && (!!errors.email && <FormError>{errors.email}</FormError>) || (!!status && <FormError>{status.email}</FormError>) }
请注意 status
的存在或值对阻止下一次表单提交没有影响。 Formik 仅中止 submission process if validation fails.
处理这种情况的另一种方法是为您的 api
错误分配一个特定的键,并使用 setStatus
来显示状态消息。
__handleSubmit = (values, {setStatus, setErrors}) => {
return this.props.onSubmit(values)
.then(() => {
setStatus("User was updated successfully.");
})
.catch((err) => {
setErrors({api: _.get(err, ["message"])});
});
}
然后任何验证错误都会出现在字段旁边,任何 api 错误可能会出现在底部:
<Formik
validationSchema={LoginSchema}
initialValues={{login: ""}}
onSubmit={this.__handleSubmit}
>
{({isSubmitting, status, errors, values, setFieldValue}) => (
<Form className={classNames("form")}>
<FormGroup>
<InputGroup>
<InputGroup.Text>
<FontAwesomeIcon icon={faUser} fixedWidth />
</InputGroup.Text>
<Field
name="login"
type={"text"}
placeholder="Login"
className="form-control"
/>
</InputGroup>
<ErrorMessage name="login" />
</FormGroup>
<Button type="submit" variant="primary" disabled={isSubmitting}>
Submit
</Button>
{errors && _.has(errors, ["api"]) && <div className="text-danger">{_.get(errors, ["api"])}</div>}
{status && <div className="text-success">{status}</div>}
</Form>
)}
</Formik>
不要忘记模式...
const LoginSchema = Yup.object().shape({
login: Yup.string()
.min(4, 'Too Short!')
.max(70, 'Too Long!')
.required('Login is required'),
});
api
错误消息将显示到 Formik 的下一次验证调用(即用户正在修复某些东西)。但是 status
消息将一直存在,直到您清除它(使用计时器或淡入淡出)。
<Formik
validationSchema={schema}
initialValues={{ email: '', pswrd: '' }}
onSubmit={(values, actions) => {
// initialise error status <---- 1
actions.setStatus(undefined);
setTimeout(() => {
// setting error status <---- 2
actions.setStatus({
email: 'This is email already exists.',
pswrd: 'This is password is incorrect',
});
}, 500);
}}
// destructuring status <---- 3
render={({ handleSubmit, handleChange, handleBlur, values, errors, status }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
name="email"
value={values['email']}
onChange={handleChange}
onBlur={handleBlur}
/>
<input
type="text"
name="pswrd"
value={values['pswrd']}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
// using error status <---- 4
{status && status.email ? (
<div>API Error: {status.email}</div>
) : (
errors.email && <div>Validation Error: {errors.email}</div>
)}
{status && status.pswrd ? (
<div>API Error: {status.pswrd}</div>
) : (
errors.pswrd && <div>Validation Error: {errors.pswrd}</div>
)}
</form>
)}
/>
const formik = useFormik({
initialValues:{
email:"",password:"",username:""
},
validationSchema:validation_schema,
onSubmit:(values) => {
const {email,password,username} = values
// ......
}
});
formik.setErrors({email:"Is already taken"}) // error message for email field