Как очистить статус перед проверкой в Formik submit?

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

В этом сценарии возникают проблемы:

  1. Пользователь отправляет формы. Проверка проходит нормально, но внутри возникает какая-то ошибка handleSubmit . В статусе отображается сообщение об ошибке
  2. Пользователь редактирует форму и повторяет попытку
  3. На этот раз возникает ошибка проверки
  4. Ошибка проверки отображается внутри поля ввода … но старое сообщение о состоянии не очищается.

Это происходит потому, что проверка выполняется перед отправкой, следовательно 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>
 );