Noitidart
Noitidart

Reputation: 37238

React.PropTypes array with specific length

Is it possible to use React.PropTypes to enforce length's on an array?

Here is a very simple case:

const TWO_NUMBERS = PropTypes.array; // i need this to be an array of two numbers

I know in javascript arrays are just objects so I tried this:

const TWO_NUMBERS = PropTypes.shape({
    0: PropTypes.number.isRequired,
    1: PropTypes.number.isRequired,
});

However this keeps telling warning me expected an object but got an array.

Upvotes: 16

Views: 16432

Answers (4)

finalfreq
finalfreq

Reputation: 6980

In this case you would need to write your own special PropTypes function which react provides you to do.

const TWO_NUMBERS = function(props, propName, componentName) {
  if (!Array.isArray(props.TWO_NUMBERS) || props.TWO_NUMBERS.length != 2 || !props.TWO_NUMBERS.every(Number.isInteger)) {
    return new Error(`${propName} needs to be an array of two numbers`);
  }

  return null
}

This would throw an error if TWO_NUMBERS isn't an array, isn't an array of two, and isn't an array of only integers.

You can get information about proptype functions here:

https://facebook.github.io/react/docs/typechecking-with-proptypes.html#react.proptypes

Its at the bottom of that example block.

Upvotes: 14

vadersfather
vadersfather

Reputation: 9319

A custom function would be the correct approach here.

  const propTypes = {
    TWO_NUMBERS: arrayOfLength.bind(null, 2)
  }

  const arrayOfLength = (expectedLength, props, propName, componentName) => {
    const arrayPropLength = props[propName].length

    if (arrayPropLength !== expectedLength) {
      return new Error(
        `Invalid array length ${arrayPropLength} (expected ${expectedLength}) for prop ${propName} supplied to ${componentName}. Validation failed.`
      )
    }
  }

Upvotes: 9

Thijs Koerselman
Thijs Koerselman

Reputation: 23280

Inspired by the answer of @finalfreq, I came up with this. It handles two numbers (floats in this case) and can also be used as arrayOf(twoNumbers). Not sure how to make it work like twoNumbers.isRequired yet...

Also I think the code is cleaner and easier to follow if you don't use the negation in the validation comparison.

import invariant from 'invariant';

function isValid(value) {
  return Array.isArray(value) && value.length === 2 && value.every(Number.isFinite);
}

export default function twoNumbers(props, propName, componentName) {
  if (Array.isArray(props)) {
    props.forEach((item, index) => {
      invariant(
        isValid(item),
        `Array item index ${index} is ${item}, but needs to be an array of two numbers`
      );
    });
  }

  const value = props[propName];

  if (!value) return; // not required so could be null

  invariant(isValid(value), `${componentName} ${propName} needs to be an array of two numbers`);
}

Upvotes: 1

Amberlamps
Amberlamps

Reputation: 40448

PropTypes checks for types not for attributes. Also, checking for PropTypes is disabled in production mode. That makes it impossible for PropTypes to check for ever changing array lengths during run-time.

Upvotes: -5

Related Questions