Describe your React propTypes as deeply as possible

On my project, I’ve been noticing code like this:

class Header extends React.Component {
  static propTypes = {
    items: PropTypes.array,
    counters: PropTypes.object,
  };
  
  // ...
}

This is how it should look like instead:

class Header extends React.Component {
  static propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape({
      id: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired,
      link: PropTypes.string.isRequired,
    })).isRequired,
    counters: PropTypes.objectOf(PropTypes.number).isRequired,
  };

  // ...
}

The difference is that in the latter component, propTypes are much more detailed. It’s better for two reasons:

  • React validates your props better. In the former component, you won’t get any warnings if you pass a wrong array into items or if you forget to pass it at all. Instead, you’ll have a wrong rendering result or a runtime error and will have to debug it.
  • You understand the interface of the component easier. This is even more important.

    With the latter component, to understand the structure of items, you just look at its propTypes. With the former one, you have to dive into its code. It’s not a problem when the component has been created just 10 minutes before, and you remember what it accepts, but it makes further support way easier.

There’s only one case when I find it acceptable to skip some propTypes definitions. It’s when your component just passes a prop to a child component and doesn’t care about its structure. In this case, the child component should validate the prop:

Note how the items
propType in Header cares only about the id field it uses, and counters doesn’t care about its items type at all.
class Header extends React.Component {
  static propTypes = {
    PropTypes.arrayOf(PropTypes.shape({
      id: PropTypes.string.isRequired,
    })).isRequired,
    counters: PropTypes.objectOf(PropTypes.any).isRequired,
  };

  render() {
    return <div>
      {this.props.items.map(item =>
        <HeaderItem item={item} counter={this.props.counters[item.id]} />
      }
    </div>;
  }
}

class HeaderItem extends React.Component {
  static propTypes = {
    item: PropTypes.shape({
      name: PropTypes.string.isRequired,
      link: PropTypes.string.isRequired,
    }).isRequired,
    counter: PropTypes.number.isRequired,
  };

  // ...
}

Here’s the rule:

Describe your propTypes as deeply as possible

Leave a Reply

Your email address will not be published.