React PropTypes: Require array to have at least one element
Asked Answered
I

2

6

I'm using the prop-types library in my React application and have the following situation:

MyComponent.propTypes = {
  foo: PropTypes.arrayOf(
    PropTypes.shape({
      bar: PropTypes.string.isRequired,
      baz: PropTypes.number.isRequired,
    })
  ).isRequired
}

By setting isRequired on foo, foo must be an array (not null/undefined). However, it is still allowed to be an empty array. In my case, I'd like to require the array to contain at least one element.

This is possible with a custom validator function:

MyComponent.propTypes = {
  foo: function (props, propName, componentName) {
    const val = props[propName]
    if (!Array.isArray(val)) return new Error(`${propName} must be an array`)
    if (val.length === 0) return new Error(`${propName} must have at least one element`)

    val.forEach(function (elem) {
      if (typeof elem.bar !== 'string') return new Error(`${propName}.bar must be a string`)
      if (typeof elem.baz !== 'number') return new Error(`${propName}.baz must be a number`)
    })
  }
}

However, it isn't pretty, and feels like it could quickly get more complicated if the array contained larger + more complex objects.

Is there a cleaner way to achieve this?

Icebox answered 28/5, 2018 at 16:8 Comment(1)
I believe it's fine as you did.Castroprauxel
K
3

You could use the PropTypes.checkPropTypes function - you will still need your validator function but the object inside the array can be described via PropTypes:

const myPropShape = {
  bar: PropTypes.string.isRequired,
  baz: PropTypes.number.isRequired,
}

MyComponent.propTypes = {
  foo: function (props, propName, componentName) {
    const val = props[propName]
    if (!Array.isArray(val)) return new Error(`${propName} must be an array`)
    if (val.length === 0) return new Error(`${propName} must have at least one element`)

    val.forEach(elem =>
      PropTypes.checkPropTypes(
        myPropShape,
        elem,
        'prop',
        `${componentName}.${propName}`,
      ),
    )
  }
}
Kovno answered 8/5, 2020 at 13:34 Comment(0)
A
1

Your post asks two questions:

  1. Is there a more clean way to validate whether a prop contains at least one element?
  2. How do you validate the props of objects inside the array?

1: Create a simple validator for the array, apply it in propTypes as normal

Create a validator in your component

const arrayNonEmpty = (props, propName) => {
  const myProp = props[propName];
  if (!Array.isArray(myProp)) return new Error(`${propName} must be an array.`);
  if (myProp.length < 1) return new Error(`${propName} must have at least one element.`);
};

Check whether the prop foo is an array of at least one element

// Use the validator as normal
MyComponent.propTypes = {
  foo: arrayNonEmpty,
};

This is the most simple, clean way to do this. Your code was very close to this but it had a few extra lines that were validating properties of the objects within the array; this code is not needed for the reasons described below.

2: Validating Objects inside the array

I'd suggest validating the properties of objects inside the array within a child component.

If you're passing an array of objects, you'll presumably be representing these objects in your view. Simply add a child component that represents an element of the array, and validate the propTypes of the object passed in to the child component.

Another option is to pass the first object of the array separately from the rest of the array. Then, it could be validated normally as any other prop.

Analyze answered 4/2, 2022 at 3:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.