#reactjs #formik
#reactjs #formik
Вопрос:
Я использую Formik с проверкой yup, следуя этому шаблону:
const handleSubmit = async (values, { setStatus }) => {
setStatus(''); // clean errors messages
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
setStatus('an error occurred '...);
}
};
const validationSchema = Yup.object({
code: Yup.string().required(t('Required')),
....
});
return (
<Formik onSubmit={handleSubmit} initialValues={initialValues} validationSchema={validationSchema}>
{({ setFieldValue, status }) => (
<Form>
....
<MyErrorComponent msg={status} />
</Form>
)}
</Formik>
);
В этом сценарии возникают проблемы:
- Пользователь отправляет формы. Проверка проходит нормально, но внутри возникает какая-то ошибка
handleSubmit
. В статусе отображается сообщение об ошибке - Пользователь редактирует форму и повторяет попытку
- На этот раз возникает ошибка проверки
- Ошибка проверки отображается внутри поля ввода … но старое сообщение о состоянии не очищается.
Это происходит потому, что проверка выполняется перед отправкой, следовательно setStatus('')
, не вызывается. Каков рекомендуемый способ справиться с этим?
Комментарии:
1. Есть ли причина, по которой вы передаете setStatus в качестве аргумента при обработке отправки, можем ли мы увидеть этот компонент
2. ИМО, я думаю, что оставить статус со старым имеет лучший UX, чем очистить его. В большинстве случаев ошибка, возникающая при отправке onSubmit, обычно является ошибкой, логическим сбоем или формой, отклоненной серверной частью (в основном технические вещи, и пользователь, который может не иметь никаких технических знаний, может не знать, что делать). Оставив его там, пользователь может увидеть, почему форма была отклонена, и связаться с ответственными лицами или обновить страницу. Поэтому я не вижу никаких причин для сброса статуса.
3. @leonbloy Если ни один из ответов не решает вашу проблему, вы можете обновить свой вопрос для текущей проблемной ситуации.
Ответ №1:
Вариант 1
Вы можете добавить validate
call в каждую форму ввода, чтобы при изменении ввода пользователем status
сообщение было удалено.
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
name: Yup.string().required("Name"),
age: Yup.number().required("Number").positive().integer()
});
const MyForm = () => (
<div>
<Formik
initialValues={{ name: "", age: "" }}
validationSchema={schema}
onSubmit={(values, actions) => {
console.log("submited", values);
actions.setStatus("")
try {
throw new Error("Something went wrong")
}
catch (error) {
actions.setStatus(error.message);
}
}}
render={({
status,
isSubmitting,
setStatus,
dirty,
values,
handleReset,
errors,
touched
}) => {
const field_props_with_validation = function() {
const name = arguments[0];
const type = arguments[1] || "text";
const placeholder = arguments[2] || "";
return {
name,
type,
placeholder,
validate: () => {
setStatus("");
}
}
}
return (
<Form>
{status}
{["name", "age"].map((field, key) => (
<div key={key}>
<Field {...field_props_with_validation(field)} />
<ErrorMessage name={field} component="div" />
</div>
))}
<button type="submit">Enter</button>
</Form>
);
}}
/>
</div>
);
function App() {
return (
<div className="App">
<h1>Registration Form</h1>
<MyForm />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Вариант 2
validation
может быть disabled
при изменении ввода формы с использованием validateOnChange
реквизитов и только validate
тогда, когда пользователь отправляет форму.
Примечание: В этом случае вам придется обрабатывать проверку, статус не будет очищен до тех пор, пока пользователь повторно не отправит форму.
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
name: Yup.string().required("Name"),
age: Yup.number().required("Number").positive().integer()
});
const MyForm = () => (
<div>
<Formik
initialValues={{ name: "", age: "" }}
validateOnChange={false}
onSubmit={async (values, actions) => {
actions.setStatus("");
await schema.validate(values, {
abortEarly: false // not to stop on single validation fail
// and return single error message
})
.then(() => {
try {
throw new Error("Some error")
}
catch (error) {
actions.setStatus(error.message);
}
})
.catch(err => {
let errors = {};
Object.keys(values).forEach((key, idx) => {
if (err amp;amp; err.errors amp;amp; err.errors[idx]) {
errors[key] = err.errors[idx];
}
});
actions.setErrors(errors)
})
}}
render={({
status,
isSubmitting,
dirty,
values,
handleReset,
errors,
touched
}) => {
return (
<Form>
{status}
{["name", "age"].map((field, key) => (
<div key={key}>
<Field name={field} type="text" placeholder={field} />
<ErrorMessage name={field} component="div" />
</div>
))}
<button type="submit">Enter</button>
</Form>
);
}}
/>
</div>
);
function App() {
return (
<div className="App">
<h1>Registration Form</h1>
<MyForm />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Ответ №2:
Я думаю, вам следует использовать что-то вроде этого:
<Formik
initialValues={
firstName: '',
email: ''
}
validationSchema={
Yup.object().shape({
email: Yup.string()
.email("Must be a valid email")
.max(255)
.required("Email is required"),
firstname: Yup.string()
.max(255)
.required("First name is required"),
})
}
onSubmit={(e, { resetForm }) => {
let val = e;
postSubmit (e.firstName,e.email);
// ur rest logic
// resetForm({
// values: {
// ...e,
// },
});
}}
>
{({ handleChange, values, initialValues, errors }) => (
// Your form jsx
)}
</Formik>
Не используйте useState
с formik, пусть Formik позаботится об ошибках и проверке!
Я использовал этот шаблон, и он отлично работает.
Если вы не нашли способ, поделитесь своей формой, я сделаю это за вас!
Ответ №3:
У него есть процесс проверки события OnMouseDown. Вы можете избежать повторной проверки, запретив по умолчанию нажимать кнопку отправки.
onMouseDown={(event: any): void => {
event.preventDefault();
}}
после этого Formik автоматически очистит статус
Ответ №4:
Вы можете контролировать, когда Formik запускает проверку, изменяя значения
<Formik validateOnChange> and / or
<Formik validateOnBlur> props depending on your needs.
По умолчанию Formik будет запускать следующие методы проверки:
Передайте вашему Formik реквизит
validateOnChange={false} and
validateOnBlur={false}
Для лучшего понимания проверьте документы Formik Validation
Ответ №5:
Лично я считаю, что статус предназначен только для уведомления пользователя. Поэтому я бы уведомил их из панели закусок / уведомлений, сообщив им об ошибке. Тогда я бы удалил их, если в форме проверки есть ошибка, чтобы они сосредоточились на исправлении неправильной
Вот пример кода и песочницы:
const validationSchema = yup.object().shape({
name: yup.string().required("Required")
});
const initialValues = {
name: "John Doe"
};
export const FormikTextField = ({ className, ...props }) => {
const [field, meta] = useField(props);
const { setStatus } = useFormikContext();
useEffect(() => {
if (meta.error) {
setStatus("");
}
}, [meta.error]);
return (
<>
<TextField
variant="outlined"
{...field}
{...props}
FormHelperTextProps={{ error: true }}
helperText={meta.error amp;amp; meta.touched ? String(meta.error) : null}
aria-invalid={Boolean(meta.error)}
/>
</>
);
};
export default function App() {
const handleSubmit = (values, { setStatus }) => {
setStatus("status set!");
};
return (
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => (
<Form>
{props.status amp;amp; <p>{props.status}</p>}
<div className="App">
<FormikTextField name="name" variant="outlined" label="Name" />
<Button type="submit" variant="contained">
Submit
</Button>
</div>
</Form>
)}
</Formik>
);
}
Ответ №6:
Да, ваше предположение верно. Проверка формы выполняется перед отправкой. Что вам нужно сделать, это добавить этот шаг очистки ошибок в пользовательскую функцию проверки как
const validate = values =>
{
// If you want some other custom validations, you can do that on values parameter
setStatus('');
};
const handleSubmit = async (values, { setStatus }) => {
// setStatus(''); // clean errors messages
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
setStatus('an error occurred '...);
}
};
const validationSchema = Yup.object({
code: Yup.string().required(t('Required')),
....
});
return (
<Formik onSubmit={handleSubmit} initialValues={initialValues} validationSchema={validationSchema}
validate={validate}>
{({ setFieldValue, status }) => (
<Form>
....
<MyErrorComponent msg={status} />
</Form>
)}
</Formik>
);
Для получения более подробной информации ознакомьтесь с этим примером CombinedValidations .
Поскольку вы используете асинхронную отправку формы, как показано в async-submission . Возможно, вам придется либо использовать функцию асинхронной проверки, создав обещание-оболочку, подобное этому, либо синхронизировать отправку формы.
Ссылки на приведенные выше примеры взяты из официальных документов.
Ответ №7:
вы можете использовать хуки для formik useFormik
import { useFormik } from 'formik';
....
const formik = useFormik({
initialValues: {},
validationSchema={validationSchema}
onSubmit: values => {
handleSubmit(values);
},
});
const handleSubmit = async (values, { setStatus }) => {
try {
... do submit work ...
const res = await sendData( convertValues(values)); //
... more work
} catch (e) {
console.error(e);
//change statuvalues or display errors
//formik.setValue...
//formik.setError...
}
};
return (
<form onSubmit={formik.handleSubmit}>
....
<button type="submit">Submit</button>
</form>
);