简体   繁体   English

如何在Formik提交验证之前清除状态?

[英]How to clear status before validation on Formik submit?

I'm using Formik with yup validation, following this pattern:我正在使用带有 yup 验证的 Formik,遵循以下模式:

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>
);

This has problems in this scenario:这在这种情况下有问题:

  1. User submits forms.用户提交 forms。 Validation passes ok, but some error occurs inside handleSubmit .验证通过,但在handleSubmit内部发生了一些错误。 An error message is shown in status状态中显示错误消息
  2. User edits form and retries用户编辑表单并重试
  3. This time a validation error occurs这次发生验证错误
  4. The validation error is displayed inside the input field...but the old status messsage is not cleared.验证错误显示在输入字段内......但旧的状态消息未清除。

This happens because validation occurs before submit, hence setStatus('') is not called.发生这种情况是因为验证发生在提交之前,因此不调用setStatus('') What's the recommended way to deal with this?处理此问题的推荐方法是什么?

I think u should use something like this:我认为你应该使用这样的东西:

<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>

Don't use useState with formik, let Formik take care of error and validation!不要将useState与 formik 一起使用,让 Formik 处理错误和验证!

I did use this pattern and it works perfectly.我确实使用了这种模式,并且效果很好。

If you didn't find the way, share your form ill make it for you!如果您没有找到方法,请分享您的表格,以便为您制作!

It has a validation process on onMouseDown event.它对 onMouseDown 事件有一个验证过程。 You can avoid duplicate validation by prevent default on Submit button.您可以通过防止提交按钮上的默认设置来避免重复验证。

 onMouseDown={(event: any): void => {
                event.preventDefault();
              }}

after that Formik automatically clear status之后Formik自动清除状态

you can use hooks for formik useFormik你可以为 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>
 );

You can control when Formik runs validation by changing the values of您可以通过更改的值来控制 Formik 何时运行验证

<Formik validateOnChange> and / or
<Formik validateOnBlur> props depending on your needs. 

By default, Formik will run validation methods as follows:默认情况下,Formik 将运行如下验证方法:

Pass to your Formik the props将道具传递给您的 Formik

validateOnChange={false} and 
validateOnBlur={false}

For better understanding check the docs Formik Validation为了更好地理解检查文档Formik Validation

Personally I think that status is only to notify user.我个人认为状态只是为了通知用户。 So I would notify them from a snackbar / notification by telling them there is an error.所以我会通过告诉他们有错误从小吃店/通知中通知他们。 Then I would remove them if there's an error on the validation form so they would focus on fixing the wrong one然后,如果验证表单上有错误,我会删除它们,以便他们专注于修复错误的表单

Here's an example code and sandbox :这是一个示例代码和沙箱


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 && 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 && <p>{props.status}</p>}
          <div className="App">
            <FormikTextField name="name" variant="outlined" label="Name" />
            <Button type="submit" variant="contained">
              Submit
            </Button>
          </div>
        </Form>
      )}
    </Formik>
  );
}

Yes, your assumption is correct.是的,你的假设是正确的。 Form validation done before submission.提交前完成表单验证。 What you have to do is, add this errors clearing step in a custom validate function as您需要做的是,在自定义验证 function 中添加此错误清除步骤

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>
);

For more details, check this CombinedValidations example.有关更多详细信息,请查看此CombinedValidations示例。

As you are using Async form submission as illustrated in async-submission .当您使用异步表单提交时,如async-submission所示。 You might have to either use async validation function by creating a wrapper Promise like this or make you form submission sync.您可能必须通过创建这样的包装器 Promise 来使用异步验证 function 或使您的表单提交同步。

Above examples' links are from official docs .以上示例的链接来自官方文档

Option 1选项1

You can add validate call in every form input so when ever user change input status message will be cleared.您可以在每个表单输入中添加validate调用,以便用户更改输入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);

Option 2选项 2

validation can be disabled on form input change using validateOnChange props and only validate when user submits the form.可以使用validateOnChange道具在表单输入更改时disabled validation ,并且仅在用户提交表单时进行validate

Note: In this case you would have to handle the validation, status will not be cleared until user resubmits the form.注意:在这种情况下,您必须处理验证,在用户重新提交表单之前,状态不会被清除。

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 && err.errors && 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM