aquaman
aquaman

Reputation: 451

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

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

Answers (4)

Joshua Pinter
Joshua Pinter

Reputation: 47481

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.

Upvotes: 2

Al Joslin
Al Joslin

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

fung
fung

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

Naga Sai A
Naga Sai A

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

Related Questions