Better Prop Validation in React

Moe Sattler
3 min readSep 1, 2016

For more stable, easier to read code

As your app grows it’s helpful to ensure that your components are used correctly. We do this by allowing you to specify propTypes.

https://facebook.github.io/react/docs/reusable-components.html

Property validation in React is a great tool that can help developers enormously to hunt down bugs. Besides being a great way of defining the component’s interface, propTypes makes it immediately clear when the source of a problem was simply faulty data passed to the props.

Unfortunately propTypes are one of the most underutilized tools in the React ecosystem. You can find many components with prop validation like this:

const { object, array } = React.PropTypesMeeting.propTypes = {
info: object.isRequired,
participants: array.isRequired,
}

This basic validation tells us that we need to pass an info object and an array of participants. But what properties do those things need in order for the Meeting component to work?

Let’s get more specific:

const { shape, number, object, arrayOf } = React.PropTypesMeeting.propTypes = {
info: shape({
timestamp: number.isRequired,
room: number,
organizer: object,
}).isRequired,
participants: arrayOf(object).isRequired,
}

Now we have a much better idea of what things we need to pass. info needs to be an object with a compulsory property timestamp and two optional ones, room and organizer. If timestamp is missing or room is the wrong type, the console will let us immediately know.

While this is pretty good already, you can take it even further:

const { shape, number, string, arrayOf } = React.PropTypesMeeting.propTypes = {
info: shape({
timestamp: number.isRequired,
room: number,
organizer: shape({
id: number.isRequired,
name: string,
}),
}).isRequired,
participants: arrayOf(shape({
id: number.isRequired,
name: string,
})).isRequired,
}

We can validate as deep as we wish. Though, the deeper we go, the harder it becomes to understand our code at a glance. How tightly we should validate properties differs from case to case. Usually, it is a good idea to validate all properties you directly use in the component, and be shallow with the rest (e.g. properties you just pass on to another component)

If we want to, we can clean this a little bit up:

const { number, string, shape, arrayOf} = React.PropTypesconst coworker = shape({
id: number.isRequired,
name: string,
})
Meeting.propTypes = {
info: shape({
timestamp: number.isRequired,
room: number,
organizer: coworker,
}).isRequired,
participants: arrayOf(coworker).isRequired,
}

We extracted the coworker shape. This is not only easier to read but gives us also the possibility to reuse validation logic. If we wanted to, we could move coworker to a seperate module and reuse it anywhere in the application.

Let’s say, instead of an array, participants would be coworkers mapped by id: There’s a Validator for that, too:

const { number, string, shape, objectOf} = React.PropTypesconst coworker = shape({
id: number.isRequired,
name: string,
})
Meeting.propTypes = {
info: shape({
timestamp: number.isRequired,
room: number,
organizer: coworker,
}).isRequired,
participants: objectOf(coworker).isRequired,
}

The objectOf validator checks if every value of an object has a certain shape. Meaning we are expecting participants to look like this:

{
1: coworker,
2: coworker,
'someKey': coworker,
...
}

By making propValidation just a little tighter, we produce more stable, easy to understand code, while saving time wasted in hunting bugs.
An alternative to propTypes is Flow, a static type checker by Facebook, which also works great with React.

Find me on Twitter! :)

--

--