Reputation: 735
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?
Upvotes: 6
Views: 3280
Reputation: 6330
Your post asks two questions:
propTypes
as normalconst 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.`);
};
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.
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.
Upvotes: 1
Reputation: 121
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}`,
),
)
}
}
Upvotes: 3