Formik Yup 验证 运行 在触摸字段之前

Formik Yup validation running before field is touched

我有一个包含字段 items 的表单,该字段存储具有属性 imagename 的对象数组。 image 属性 接受一个文件但是是可选的,name 字段是必需的。

出于某种原因,当我触摸名称字段时,图像字段验证开始并抛出错误“图像太大”,这可以从 formik 的错误对象中看出。我不明白为什么会这样。下面是组件的代码和代码框,您可以在此处查看 https://codesandbox.io/s/new-fog-xs2wb?file=/src/components/form.js

import React from 'react';
import { Formik, Field, FieldArray } from 'formik';
import * as Yup from 'yup';

function MyForm(props) {
  const FILE_SIZE = 5 * 1024 * 1024;
  const SUPPORTED_FORMATS = ["image/jpg", "image/jpeg", "image/gif", "image/png"];

  const validationSchema = Yup.object().shape({
    items: Yup.array().of(Yup.object().shape({
      image: Yup.mixed()
          .test("fileSize", "Image is too large", (value) => value && value.size <= FILE_SIZE)
          .test(
            "fileFormat",
            "Unsupported Format - We only allow images.",
            (value) => value && SUPPORTED_FORMATS.includes(value.type)
          ),
       name: Yup.string().required('Required') 
    }))
  });

  const initialValues = { items: [{
    image: undefined,
    name: ''
  }]}

  return (
    <div className="container">
      <Formik enableReinitialize={true}
              initialValues={initialValues}
              validationSchema={validationSchema}
              onSubmit={async (values, { setSubmitting, resetForm }) => {
                setSubmitting(true);
                console.log(values);
                setSubmitting(false);
              }}>
              {({
                values,
                errors,
                touched,
                handleChange,
                handleBlur,
                handleSubmit,
                isSubmitting,
                setFieldValue,
              }) => (
                <form className="mt-3" onSubmit={handleSubmit}>
                    <FieldArray name="items">
                    {({ push, remove }) => (
                      <React.Fragment>
                      {values.items && values.items.length && values.items.map((item, index) => (
                        <div key={index}>
                          {/* <Field name={`items[${index}].image`} /> */}
                          <div className="form-group">
                              <label htmlFor="name">Name</label>
                              <input name={`items[${index}].name`} className="form-control" id="name" aria-describedby="nameHelp" onChange={handleChange} onBlur={handleBlur} />
                              <small id="nameHelp" className="form-text text-muted">Enter a descriptive name for the item.</small>
                          </div>
                          <button type="button" className="btn btn-danger mr-2 mb-2" onClick={() => remove(index)}>Remove</button>
                        </div>
                      ))}
                      <button type="button" className="btn btn-secondary mr-2 mt-4" onClick={() => push({image: undefined, name: ''})}>Add</button>
                      <button type="submit" disabled={isSubmitting} className="btn btn-primary mt-4">Submit</button>
                      <pre className="mt-2">{JSON.stringify(errors, null, 2)}</pre>
                      </React.Fragment>
                    )}
                    </FieldArray>
                </form>
              )}
      </Formik>
    </div>)
}

export default MyForm;

如何让它按预期工作? 非常感谢您的帮助。

Formik 提供了许多用于验证的实用程序,您可以使用以下组合来达到您的目的:

validateOnChange={false}
validateOnBlur={true}

这将解决您的问题,如果无论如何都不起作用,请删除 validateOnBlur

您在测试方法中使用的函数 应该 return false 以显示 resp 错误消息 。参考:https://github.com/jquense/yup#mixedtestoptions-object-schema

所以你应该这样做 !(value?.size > FILE_SIZE)
!(value && !SUPPORTED_FORMATS.includes(value.type)) 使其发挥作用!

代码更新:

image: Yup.mixed()
          .test(
             "fileSize", 
             "Image is too large",
             (value) => !(value?.size > FILE_SIZE)
          )
          .test(
            "fileFormat",
            "Unsupported Format - We only allow images.",
            (value) => !(value && !SUPPORTED_FORMATS.includes(value.type))
          ),