Reputation: 451
This is the use case: A component TableGroup
should require a user to specify data
property which is an array of objects to be rendered in the table or requestDataUrl
property from where the component will get that array of objects. In short, one of these two properties is required but not both. How could I achieve that in the following component.propTypes
object?
TableGroup.propTypes = {
fieldNames: React.PropTypes.array.isRequired,
dataFields: React.PropTypes.array.isRequired,
uniqueField: React.PropTypes.string.isRequired,
data: React.PropTypes.array,
requestUrlSource: http://someurl/api/resource
}
Upvotes: 5
Views: 3168
Reputation: 47481
isRequiredIf
.There is a PR from 4 years ago by @evcohen that added isRequiredIf
to the PropTypes library. Unfortunately, even at that time they were putting the PropTypes library in maintenance mode and would not merge it in.
The company I work for still uses PropTypes and so we forked the master
branch of the PropTypes library and added this functionality in.
So now you can do something like this:
data: PropTypes.array.isRequiredIf( props => !props.requestUrlSource ),
requestUrlSource: PropTypes.string.isRequiredIf( props => !props.data )
Super clean and minimal.
Feel free to use our fork in your own project by updating your package.json
with the following:
"prop-types": "github:cntral/prop-types#isRequiredIf"
NOTE: It does not take a boolean param, only a function that is passed the props and needs to return a boolean.
Upvotes: 2
Reputation: 783
I wrote an NPM module for this: https://www.npmjs.com/package/react-either-property
The code maintains the type checking options, offers EitherOptional and EitherRequired strategies -- it supports combining multiple usages in one props definition.
Note: the custom rule's property name is a throwaway, and its usage as an actual property is undefined.
import { EitherOptional, EitherRequired } from 'react-either-property';
[ module code goes here]
ComponentSeven.propTypes = {
east: PropTypes.number,
west: PropTypes.number,
north: PropTypes.number,
south: PropTypes.number,
ignored: EitherOptional('north', 'south'),
undefined: EitherRequired('north', 'south'),
};
Upvotes: 0
Reputation: 639
According to React's doc, I think customProp
should work perfectly for you.
dataOrRequest: function(props, propName, componentName) {
function checkDataOrRequest() {
return (!props.hasOwnProperty('data')
&& !props.hasOwnProperty('requestUrlSource'))
&& new Error(`Either "data" or "requestUrlSource" is required`);
}
function checkTypes() {
if ((propName === 'data' && props.constructor !== Array) ||
(propName === 'requestUrlSource' && props.constructor !== String)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
return false;
}
return checkDataOrRequest() && checkTypes();
}
after the declaration of your custom validation fn, now you could use it in
TableGroup.propTypes = {
data: dataOrRequest,
requestUrlSource: dataOrRequest
}
Upvotes: 3
Reputation: 10975
To achieve expected result, use below option
function dataOrRequest(props, propName, componentName) {
return (!props.hasOwnProperty('data') &&
!props.hasOwnProperty('requestUrlSource'))
&& new Error(`Either "data" or "requestUrlSource" is required`);
}
TableGroup.propTypes = {
fieldNames: React.PropTypes.array.isRequired,
dataFields: React.PropTypes.array.isRequired,
uniqueField: React.PropTypes.string.isRequired,
data: dataOrRequest,
requestUrlSource: dataOrRequest
}
Upvotes: 7