Conditional React PropTypes
Asked Answered
I

4

6

I have a React component which I would like to enforce one of two props to be provided - if neither is provided I'd like to be able to trigger a PropType warning:

MyComponent.propTypes = {
  firstProp: PropTypes.string.isRequired, // required only if `otherProp` not provided
  otherProp: PropTypes.string.isRequired, // required only if `firstProp` not provided
}

I believe this is possible with AirBnB's prop-types but I'm wondering if this can be done with only React PropTypes.

Interrex answered 22/10, 2018 at 18:20 Comment(0)
R
6

There is an npm for everything. Here is one for conditional prop types based on props. Here is an example of how I used it in a project for determining ad size prop types.

import React, { Component } from 'react';
import isRequiredIf from 'react-proptype-conditional-require';
import PropTypes from 'prop-types';

class AdSlot extends Component {
  // React component stuff
}

const slotPropTypes = PropTypes.oneOfType([
  PropTypes.string, // e.g. 'fluid'
  PropTypes.arrayOf(PropTypes.number), // e.g. [300, 250]
  PropTypes.arrayOf(PropTypes.array), // e.g. [[300, 250], [1650, 300]]
  PropTypes.arrayOf(PropTypes.oneOfType([ // e.g. ['fluid', [300, 250]]
    PropTypes.string.isRequired,
    PropTypes.array,
  ])),
]);

const responsiveSizesPropTypes = PropTypes.arrayOf(PropTypes.oneOfType([
  PropTypes.number,
  PropTypes.array,
  PropTypes.string,
]));

const doesNotHaveResponsiveSizes = props => !(props.hasOwnProperty('responsiveSizes'));
const doesNotHaveSlotSizes = props => !(props.hasOwnProperty('slotSizes'));

AdSlot.proptypes = {
  slotSizes: isRequiredIf(slotPropTypes, doesNotHaveResponsiveSizes),
  responsiveSizes: isRequiredIf(responsiveSizesPropTypes, doesNotHaveSlotSizes),
}

export default AdSlot;
Reseda answered 22/10, 2018 at 18:25 Comment(3)
I was looking for a way without an additional install - but it's likely that I'll need a librar, so I'll bear this approach in mind if it's not possible with only react-proptypes.Interrex
@Interrex have a look under the hood. This npm is only one file. Maybe you can just "borrow" the parts of the functionality you want: github.com/evcohen/react-proptype-conditional-require/blob/…Reseda
hrm, that's true.. I hadn't thought to look at the source.. I'll see if I can pull out the parts that I need - I'll mark this as accepted answer.. thanks!Interrex
P
1

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:

MyComponent.propTypes = {
  firstProp: PropTypes.string.isRequiredIf( props => !props.otherProp ), // required only if `otherProp` not provided
  otherProp: PropTypes.string.isRequiredIf( props => !props.firstProp ), // required only if `firstProp` not provided
}

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.

Primulaceous answered 13/8, 2020 at 16:35 Comment(0)
D
0

Here the original question is probably about JavaScript React, but if you're writing TS React code and letting TypeScript checking your props types, you can use a discriminated type union to describe your needs.

type MyComponentProps =
    | { firstProp?: string, otherProp?: never }
    | { firstProp?: never, otherProp?: string }
<MyComponent /> // OK!
<MyComponent firstProp="..."/> // OK!
<MyComponent otherProp="..."/> // OK!
<MyComponent firstProp="..." otherProp="..."/> // TS Error!

Duhamel answered 25/11, 2022 at 14:56 Comment(0)
R
0

you can apply this implementation to many different cases. For example if you pass 10 props but you only want to accept some of them, then you need to determine the count variable in the implementation

MyComponent.propTypes = {
  firstProp: PropTypes.string,
  otherProp: PropTypes.string,
  // we are destructuring the props
  checkOnlyOneVariant: ({ firstProp, otherProp }) => {
    const count = Number(!!firstProp) + Number(!!otherProp);
    // if you passed both, count will be 2. but we do not want count to be greater than 1
    if (count > 1) {
      return new Error("MyComponent accepts either of firstProp or otherProp");
    }
  },
};
Rodent answered 10/12, 2022 at 0:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.