51

I have a component <Button>.
If the component doesn't has this.props.children, I want to set the prop ariaLabel as isRequired, otherwise in can be optional. How do I do that?

ariaLabel prop not required:

<Button>Add to bag</Button>

ariaLabel prop has to be required:

<Button ariaLabel="Add to bag" icon={ favorite } />

if this.props.children and this.props.ariaLabel are empty, it throws an error saying that this.props.ariaLabel is isRequired

<Button icon={ favorite } />

propTypes:

Button.propTypes = {
    /** icon inside Button. */
    icon: React.PropTypes.object,
    /** Content inside button */
    children: React.PropTypes.node,
    /** Aria-label to screen readers */
    ariaLabel: React.PropTypes.string, /*isRequired if children is empty */
};

Thanks

sandrina-p
  • 3,794
  • 8
  • 32
  • 60
  • 1
    You will need something like https://github.com/evcohen/react-proptype-conditional-require to do it in the propTypes object. – Joe Feb 17 '17 at 13:39

4 Answers4

140

You don't need another library, 'prop-types' provides this out of the box. See https://facebook.github.io/react/docs/typechecking-with-proptypes.html

Example:

import PropTypes from 'prop-types';

//.......    

ExampleComponent.propTypes = {
    showDelete: PropTypes.bool,
    handleDelete: function(props, propName, componentName) {
        if ((props['showDelete'] == true && (props[propName] == undefined || typeof(props[propName]) != 'function'))) {
            return new Error('Please provide a handleDelete function!');
        }
    },

}
Lucas P.
  • 180
  • 1
  • 6
chickenchilli
  • 3,358
  • 2
  • 23
  • 24
  • 6
    This is what I was looking for. I don't want another dependency. – grammar Oct 04 '17 at 21:30
  • 2
    Given that `prop-types` does can do this out of the box this is clearly the best answer. – kylebebak Dec 21 '17 at 20:36
  • Is there a good way to use the propTypes forms inside this? I am thinking of things like `node` or `shapeOf(...)`, where the test is moderately complicated to write out. – Troy Daniels Mar 06 '18 at 21:40
  • This is exactly what react-required-if library does. It's just one file with a dozen lines of code that provides a much better interface so you don't need to write this ugly thing in your own code. If you don't to add another line to your package.json, I'd advice to copy that function and put it in your helper files – Pietro Coelho Mar 28 '18 at 18:33
  • 7
    Too much verbosity, i think prop-types package itself should include something like this. – tommyalvarez Apr 27 '18 at 18:27
  • 1
    I agree. prop-types should provide this out of the box. – developarvin Jun 11 '18 at 05:58
  • This is nice, can obviously be extracted to a helper function. But unfortunately doesn't support actual type checking. Should really call the original validation function somewhere – Martin B. Sep 17 '18 at 14:15
  • Yeah, what I need is propTypes.Boolean.allowIf(some other prop is not null). Your thing does it, but doesn't work with IDE type ahead... which is really the only reason to use proptypes since they are or should be removed by your compilation during packing... they are to assist the developer only, JS isn't strongly typed for a reason... – gunslingor Sep 06 '22 at 18:19
17

This may be exactly what you need: https://github.com/thejameskyle/react-required-if

In your case, your propTypes would be:

import requiredIf from 'react-required-if';

Button.propTypes = {
    /** icon inside Button. */
    icon: React.PropTypes.object,
    /** Content inside button */
    children: React.PropTypes.node,
    /** Aria-label to screen readers */
    ariaLabel: requiredIf(React.PropTypes.string, props => !props.children), /*isRequired if children is empty */
};
Kelvin De Moya
  • 5,118
  • 1
  • 17
  • 16
  • 3
    why using third party here! – Jalal Nov 18 '17 at 15:32
  • 2
    You don't need another untested dependency in your package if you care about your project long-term. – katsos Jun 17 '18 at 16:04
  • 5
    This third party code is awesome, I just extracted the code to make my own custom function to avoid adding a dependency – deb0ch Sep 28 '18 at 09:49
  • 3
    This third party library is literally 18 lines long and easily understood. There's nothing wrong with using this. If you don't like using a dependency, just take 15 minutes and extract it out into your own helper or something. Even better, if you don't like using an "untested" dependency, create a fork of the repo, add some tests and then submit a PR. _Leave the world a little better than when you arrived._ – Joshua Pinter Aug 12 '20 at 14:22
9

To add to @chickenchilli's answer above, you could abstract this into a more handy helper function like this:

conditionalPropType.js

export default function conditionalPropType(condition, message) {
  if(typeof condition !== 'function') throw "Wrong argument type 'condition' supplied to 'conditionalPropType'";
  return function(props, propName, componentName) {
    if (condition(props, propName, componentName)) {
      return new Error(`Invalid prop '${propName}' '${props[propName]}' supplied to '${componentName}'. ${message}`);
    }
  }
}

MyComponent.js

import PropTypes from 'prop-types';
import conditionalPropType from './conditionalPropType';

[...]

MyComponent.propTypes = {
  conditionProp: PropTypes.bool,
  dependentProp: conditionalPropType(props => (props.condition && typeof(props.someProp) !== 'boolean'), "'dependentProp' must be boolean if 'conditionProp' is true"),
};
coconup
  • 945
  • 10
  • 17
2

Use isRequiredIf.

There is a PR from 4 years ago 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:

ariaLabel: PropTypes.string.isRequiredIf( props => props.children )

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.

Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245