Search code examples
reactjsreact-proptypes

React Prop Types Different Required Props Depending on Flag


I have a React component that gets a configuration object as a prop, it looks something like this:

{
    active: true,
    foo: {
         bar: 'baz'
    }
}

In some cases, I want to disable the feature that the component displays by passing in a different object with active: false, like this:

{
    active: false
}

This works fine and is not the problem.

However, I also want to make sure that client code that uses my component supplies the proper configuration object:

  • if active is true, foo is required
  • if active is false, foo is optional and does not need to be provided

How do I define prop types for a case like this?

I've tried:

MyComponent.propTypes = {
    config: PropTypes.oneOf([
        {
            active: false
        },
        PropTypes.shape({
            active: true,
            foo: PropTypes.shape({
                bar: PropTypes.string.isRequired
            })
        }).isRequired
    ]).isRequired
};

But this gives me the following warning:

Warning: Failed prop type: Invalid prop config of value [object Object] supplied to MyComponent, expected one of [{"active":true},null].

in MyComponent

I know why this does not work: it's because PropTypes.oneOf does not expect dynamic prop type matchers as values, but simply an array of valid arguments.

The question is, is there a way to make this work?

I've made a runnable sandbox example where you can try out the example code above: https://codesandbox.io/s/n9o0wl5zlj


Solution

  • As wgcrouch suggested in his answer, the prop-types lib does not provide this functionality, so using a custom prop type is the way to go.

    Fortunately, as Tom Fenech pointed out in the comments to my question, this particular problem has already been solved, so there is an npm package available that I've used: react-required-if.

    My working solution looks like this:

    import React from 'react';
    import PropTypes from 'prop-types';
    import requiredIf from 'react-required-if';
    
    function MyComponent({ config }) {
      if (!config.active) {
        return null;
      }
      return <h1>Hello {config.foo.bar}!</h1>;
    }
    
    MyComponent.propTypes = {
      config: PropTypes.shape({
        active: PropTypes.bool.isRequired,
        foo: requiredIf(
          PropTypes.shape({
            bar: PropTypes.string.isRequired
          }),
          props => props.active
        )
      })
    };
    
    export default MyComponent;
    

    → See updated code sandbox with solution.