jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.87k stars 2.79k forks source link

FieldArray - outer validation chars show up in inner field errors #3104

Open jebax opened 3 years ago

jebax commented 3 years ago

Bug report

I'm using the FieldArray component in combination with a Yup validation schema. I have a FieldArray of friends, where each friend is a string.

In my validation schema, I specify that each friend is required(), and that the array as a whole must have a max() of 2 friends.

Current Behavior

When the inner validation passes, but the outer validation fails, the chars from the outer validation message are parsed as the error messages for the inner fields, in order.

My validation schema:

Yup.object().shape({
  friends: Yup.array().of(
    Yup.string().required("Required")
  ).max(2, "Max 2 friends")
})

Here's what happens if you log out the errors in the inner and outer fields

const { errors, values: { friends } } = useFormikContext()
const [outerField, outerMeta] = useField("friends")
const [firstFriendField, firstFriendMeta] = useField("friends[0]")

console.log(errors) // { friends: "Max 2 friends" }
console.log(friends) // ["friend1", "friend2", "friend3"]
console.log(outerMeta.error) // "Max 2 friends"
console.log(firstFriendMeta.error) // "M"

Screenshot

Screenshot 2021-03-18 at 14 18 21

Expected behavior

Errors for inner fields shouldn't get characters from the outer field error message:

const { errors, values: { friends } } = useFormikContext()
const [outerField, outerMeta] = useField("friends")
const [firstFriendField, firstFriendMeta] = useField("friends[0]")

console.log(errors) // { friends: "Max 2 friends" }
console.log(friends) // ["friend1", "friend2", "friend3"]
console.log(outerMeta.error) // "Max 2 friends"
console.log(firstFriendMeta.error) // ""

Reproducible example

https://codesandbox.io/s/formik-codesandbox-template-forked-ehijy?file=/index.js

Suggested solution(s)

<Field /> / useField could maybe parse the field name, or check if the field has a FieldArray as a parent, to detect whether a field is part of an array. If so, the nested field's meta.error should be empty if the array field's error is a string.

Your environment

Software Version(s)
Formik 2.1.6
React 16.9.0
npm/Yarn yarn
github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 60 days

jebax commented 3 years ago

anyone?

ferdiamg commented 3 years ago

Hmm @jebax did you find a solution? Seems like the same is kind of happening here: https://github.com/formium/formik/issues/3178

jebax commented 3 years ago

@ferdiamg here's a solution I'm using for now. It's not amazing and only works if your form fields are nested 1 level deep, but it works for me:

const ExampleField = ({ Component, ...props }) => {
  const [field, meta] = useField(props)
  const { errors } = useFormikContext()

  const error = getError(errors, meta.error, props.name)

  return <Component {...props} {...meta} {...field} error={error} />
}

const objectNestingSeparatorRegex = /\.|\[/

const getError = (formErrors, fieldError, name) => {
  if (name.match(objectNestingSeparatorRegex)) {
    const outerFieldName = name.split(objectNestingSeparatorRegex)[0]
    const outerFieldError = formErrors[outerFieldName]

    // When no error messages for inner fields
    if (!Array.isArray(outerFieldError)) {
      return undefined
    }
  }

  return fieldError
}
johnrom commented 3 years ago

The same way we are selecting values.arrayField[0], we are selecting errors.arrayField[0]. Well, strings are like arrays of characters, so errors.yourArray[0] returns the first letter.

I don't have a solution atm, but that is the underlying issue. We should probably:

These two things work in completely different ways and some people will probably prefer one over the other. I prefer the latter. I think the developer should choose a convention of adding errors and checking them at the root or at independent array items.

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 60 days

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 60 days