Reputation: 295
I am using prop-types in a react component. The component has a prop data
which should be an array of objects like this: [{value: 'some string', id: 123}]
. I want to add a custom prop-type function that makes data
required if props.useCustomSuggestions
is false
. I tried something like this:
data: (props) => {
if (!props.useCustomSuggestions && !props.data) {
return new Error('Data must be provided to use default suggestions');
} else if (props.data && typeof props.data !== 'object') {
return new Error(
'Data must be an array',
);
}
};
I believe this works to validate that the array is required when props.useCustomSuggestions
is false
, but it doesn't check wether the objects in data
are formatted correctly. Is there another way to write this that validates the array is composed of objects with properties value
of type string and id
of type number?
I thought the customArrayProp documentation in react might be the solution, but it doesn't pass the entire props object so I lose the ability to make data
conditionally required on the value of useCustomSuggestions
.
Upvotes: 1
Views: 1188
Reputation: 23705
There is a special way to call propTypes
check directly for reusing their check logic: PropTypes.checkPropTypes()
data: (props, propName, componentName) => {
if (props.useCustomSuggestion) { // data should be validated but is optional
PropTypes.checkPropTypes({
[propName]: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
})
)
},
props,
propName,
componentName
);
} else { // data is required
PropTypes.checkPropTypes({
[propName]: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
})
).isRequired
},
props,
propName,
componentName
);
}
}
PS for unknown reason codesandbox sometimes run propTypes
check and sometimes silently skipped that, so I'm not 100% my code sample works. But I've checked approach with string
/number
- just when I was trying to adopt that to arrayOf
/shape
it started to go weird.
Probably you will be able to move common part(shape
's internal) to interm variable to reduce code duplication, but as I said was unable to ensure that.
Upvotes: 1
Reputation: 295
This seems to do the trick, if anyone has a more concise answer, or feedback on the below code please still comment or post your own answer.
data: (props) => {
if (!props.useCustomSuggestions && !props.data) {
return new Error('Data must be provided to use default suggestions');
} else if (props.data && !Array.isArray(props.suggestions)) {
return new Error('Data must be an array');
} else if (props.data.length > 0) {
for (let i = 0; i < props.data.length; i += 1) {
if (!props.data[i].value || !props.data[i].id) {
return new Error(
'Objects in data must include properties "value" and "id"',
);
} else if (
typeof props.data[i].value !== 'string' ||
typeof props.data[i].id !== 'number'
) {
return new Error(
'Objects in data array must have property "value" of type string, and "id" of type number',
);
}
}
}
},
Upvotes: 0