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:
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 = {
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: