How to validate nested proptypes with React

November 20th, 2015

So you're writing a React component. You know how to pass props to your component, and you know how to reference the props in the component with this.props.propname.

But there's something in the middle that's a little tricker: propTypes.

In this article I'll answer questions that fit this formula:

I have an object like X... how do I create a propType for it?

You have probably already found out from the React Prop Validation docs how to set simple props, like strings and bools:

propTypes: {
  myString: React.PropTypes.string,
  myBool: React.PropTypes.bool,
}

But what if your property is more complicated than that? What if you want to pass a property like this to your component?

{
  text: 'hello',
  numbers: [1, 2, 3],
}

You COULD do use React.PropTypes.object:

propTypes: {
  myObject: React.PropTypes.object,
}

And that would work, but you would basically be throwing away the great React validation features. Wildly invalid values would still pass React's prop validation with the above setup.

To the rescue: shape() and arrayOf()

PropTypes.shape() and PropTypes.arrayOf() are both super useful. These 2 handy methods are in the docs, but they are not given the emphasis that they deserve. These propTypes would correctly validate the above object:

propTypes: {
  myObject: React.PropTypes.shape({
    text: React.PropTypes.string,
    numbers: React.PropTypes.arrayOf(React.PropTypes.number)
  });
}

Exercise

I'll leave an exercise to the reader - using what you learned - can you write the propTypes that would validate this object?

[
  {
    foo: "hello",
    bar: 3,
    baz: true
  },
  {
    foo: "world",
    bar: 7
  },
  {
    foo: ":)",
    bar: 0,
    baz: false
  }
];

Answer

Don't cheat! Are you really done?

...

I'll leave some space here...

...

Are you finished yet?

...

Okay, how'd you do? There is more than one correct answer, but you should have something similar to this:

React.PropTypes.arrayOf(
  React.PropTypes.shape({
    foo: React.PropTypes.string.isRequired,
    bar: React.PropTypes.number.isRequired,
    baz: React.PropTypes.bool
  })
);

I threw in the isRequired checks because it's so common to have an object with some required and some optional properties.