'React PropTypes: Require array to have at least one element
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?
Solution 1:[1]
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}`,
),
)
}
}
Solution 2:[2]
Your post asks two questions:
- Is there a more clean way to validate whether a prop contains at least one element?
- 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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
