我正在使用React与后端通信。现在尝试正确实现Formik(表单库)。
主要问题: 如何正确使用Formik的setError方法?
客户端验证错误可以正确显示,但是现在我尝试设置/显示后端验证错误,这些错误将通过状态码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>
);
}
答案 0 :(得分:5)
Formik作者在这里...
setError
为deprecated in v0.8.0,并重命名为setStatus
。您可以在setErrors(errors)
函数中使用setStatus(whateverYouWant)
或handleSubmit
来获得所需的行为,如下所示:
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
)。
IMHO 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。
答案 1 :(得分:1)
我刚刚解决了自己的问题。
我需要使用:
class Class1:
def __init__(self):
self.my_data = []
}
def set_data(self,alarm_id,timestamp):
self.my_data = [{
"alarm_id":alarm_id,
"timestamp":timestamp
}]
return 5
def get_data(self):
return self.my_data
def main(argv):
myObj = Class1()
set_data_udf = func.udf(myObj.set_data, IntegerType())
df = df \
.withColumn("result", set_data_udf(
func.col("alarm_id"),
func.col("timestamp")
)
)
print(myObj.get_data())
if __name__ == '__main__':
main(sys.argv[1:])
代替:
setErrors( errors )
答案 2 :(得分:0)
处理这种情况的另一种方法是为您的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
消息将一直保留,直到您将其清除(使用计时器或淡入淡出)为止。
答案 3 :(得分:0)
<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>
)}
/>
答案 4 :(得分:0)
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