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
itemsor 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:
itemspropType 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 = {
items: 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:
The Ghanaian’s time at Panathinaikos was marred by injury and an ongoing legal dispute.