【问题标题】:How to clear status before validation on Formik submit?如何在Formik提交验证之前清除状态?
【发布时间】:2021-03-08 08:04:23
【问题描述】:

我正在使用带有 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>
);

这在这种情况下有问题:

  1. 用户提交表单。验证通过,但handleSubmit 内部发生了一些错误。状态中显示错误消息
  2. 用户编辑表单并重试
  3. 这次出现验证错误
  4. 验证错误显示在输入字段内...但旧的状态消息未清除。

这是因为验证发生在提交之前,因此不会调用setStatus('')。 处理此问题的推荐方法是什么?

【问题讨论】:

  • 处理提交时是否有理由将 setStatus 作为参数传递,我们可以看到该组件
  • IMO,我认为将状态留给旧状态具有比清除状态更好的用户体验。在大多数情况下,onSubmit 上发生的错误通常是错误、逻辑错误或表单被后端拒绝(主要是技术人员和可能没有任何技术知识的用户可能不知道该怎么做)。通过将其留在那里,用户可以查看表单被拒绝的原因并继续联系负责人或刷新页面。所以我看不出有任何理由重置状态。
  • @leonbloy 如果没有一个答案能解决您的问题,您可以针对当前的问题情况更新您的问题。

标签: reactjs formik


【解决方案1】:

我认为你应该使用这样的东西:

<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 处理错误和验证!

我确实使用了这种模式,并且效果很好。

如果您没有找到方法,请分享您的表格,为您制作!

【讨论】:

    【解决方案2】:

    它对 onMouseDown 事件有一个验证过程。您可以通过阻止默认提交按钮来避免重复验证。

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

    之后Formik自动清除状态

    【讨论】:

      【解决方案3】:

      您可以通过更改的值来控制 Formik 何时运行验证

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

      默认情况下,Formik 会运行如下验证方法:

      将道具传递给您的 Formik

      validateOnChange={false} and 
      validateOnBlur={false}
      

      为了更好地理解,请查看文档Formik Validation

      【讨论】:

        【解决方案4】:

        我个人认为状态只是为了通知用户。所以我会通过告诉他们有错误从小吃店/通知中通知他们。然后,如果验证表单上有错误,我会删除它们,以便他们专注于修复错误的表单

        这是一个示例代码和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>
          );
        }
        
        

        【讨论】:

          【解决方案5】:

          是的,你的假设是正确的。提交前完成表单验证。您需要做的是,将此错误清除步骤添加到自定义验证功能中

          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 中所示的异步表单提交时。您可能必须通过创建像this 这样的包装器 Promise 来使用异步验证功能,或者让您的表单提交同步。

          以上示例的链接来自official docs

          【讨论】:

            【解决方案6】:

            选项 1

            您可以在每个表单输入中添加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);
            

            选项 2

            validation 可以在使用validateOnChange 道具更改表单输入时为disabled,并且只有在用户提交表单时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 && 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);
            

            【讨论】:

              【解决方案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>
               );
              

              【讨论】:

                猜你喜欢
                • 2020-09-05
                • 2012-10-15
                • 2021-10-29
                • 2012-06-30
                • 2015-03-29
                • 1970-01-01
                • 2017-01-15
                • 2020-11-11
                • 2014-09-05
                相关资源
                最近更新 更多