Search code examples
javascriptreactjsreact-proptypes

Is it possible to write custom prop-type validator for conditionally required array of objects without losing object validation?


I am using prop-types in a react component. The component has a prop data which should be an array of objects like this: [{value: 'some string', id: 123}]. I want to add a custom prop-type function that makes data required if props.useCustomSuggestions is false. I tried something like this:

data: (props) => {
  if (!props.useCustomSuggestions && !props.data) {
    return new Error('Data must be provided to use default suggestions');
  } else if (props.data && typeof props.data !== 'object') {
    return new Error(
      'Data must be an array',
    );
  }
};

I believe this works to validate that the array is required when props.useCustomSuggestions is false, but it doesn't check wether the objects in data are formatted correctly. Is there another way to write this that validates the array is composed of objects with properties value of type string and id of type number?

I thought the customArrayProp documentation in react might be the solution, but it doesn't pass the entire props object so I lose the ability to make data conditionally required on the value of useCustomSuggestions.


Solution

  • There is a special way to call propTypes check directly for reusing their check logic: PropTypes.checkPropTypes()

    data: (props, propName, componentName) => {
      if (props.useCustomSuggestion) {  // data should be validated but is optional
        PropTypes.checkPropTypes({ 
            [propName]: PropTypes.arrayOf(
              PropTypes.shape({
                value: PropTypes.string.isRequired,
                id: PropTypes.number.isRequired
             }) 
            ) 
          }, 
          props, 
          propName, 
          componentName
        );
      } else { // data is required
        PropTypes.checkPropTypes({ 
            [propName]: PropTypes.arrayOf(
              PropTypes.shape({
                value: PropTypes.string.isRequired,
                id: PropTypes.number.isRequired
             }) 
            ).isRequired
          }, 
          props, 
          propName, 
          componentName
        );
      }
    }
    

    PS for unknown reason codesandbox sometimes run propTypes check and sometimes silently skipped that, so I'm not 100% my code sample works. But I've checked approach with string/number - just when I was trying to adopt that to arrayOf/shape it started to go weird.

    Probably you will be able to move common part(shape's internal) to interm variable to reduce code duplication, but as I said was unable to ensure that.