How to define alternative required property in a React component PropTypes?
Asked Answered
S

4

5

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
}
Subjacent answered 6/5, 2017 at 6:12 Comment(2)
in the mentioned propTypes, either dataFields or requestUrlSource right?Bridgman
No, either requestUrlSource or data. Only one of these should be required. But not the both. @NagaSaiASubjacent
A
2

Use 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.

Albescent answered 13/8, 2020 at 16:34 Comment(0)
B
7

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
}
Bridgman answered 6/5, 2017 at 7:8 Comment(2)
That's great! How could you then further check for the propTypes of data or requestUrlSource in your example? let's say, you want to ensure that data is an object of a certain shape?Ursi
would you then just check for the PropTypes of both data and requestUrlSource as optional props?Ursi
M
3

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
}
Magnetron answered 6/5, 2017 at 7:30 Comment(0)
A
2

Use 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.

Albescent answered 13/8, 2020 at 16:34 Comment(0)
S
0

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'),
};
Scuba answered 5/6, 2020 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.