This post is part of the series 30 Days of React.

In this series, we're starting from the very basics and walk through everything you need to know to get started with React. If you've ever wanted to learn React, this is the place to start!

PropTypes

Edit this page on Github

We're looking at how to make reusable React components today so that not only can we share our components across apps and teams.

Phew! We made it to week two (relatively unscathed)! Through this point, we've talked through most of the basic features of React (props, state, life-cycle hooks, JSX, etc.). In this section, we're going to look a bit at annotating our components.

PropTypes

You may have noticed we use props quite a bit in our components. For the most part, we'll expect these to be a particular type or set of types (aka an object or a string). React provides a method for defining and validating these types that allow us to easily expose a component API.

Not only is this a good practice for documentation purposes, it's great for building reusable react components.

The React.PropTypes object exports a bunch of different types which we can use to define what type a component's prop should be. We can define these using the propTypes method in the ES6 class-style React prop:


class Clock extends React.Component {
  // ...
}

Clock.propTypes = {
  // key is the name of the prop and
  // value is the PropType
}

Using the React.createClass() form, we'll define a key called propTypes. For instance, we can rewrite the form of the Clock component as:

const Clock = React.createClass({
  proptypes: {}
});

From within this prop, we can define an object which has the key of a prop as the name of the prop we are defining and a value defines the type (or types) it should be defined as.

For instance, the Header component we built a few days ago accepts a a prop called title and we expect it to be a string. We can define it's type to be a string as such:

class Header extends React.Component {
  // ...
}

Header.propTypes = {
  title: React.PropTypes.string
}

React has a lot of types to choose from, exported on the React.PropTypes object and even allows for us to define a custom object type. Let's look at an overall list of available types:

Basic types

React exposes a few basic types we can use out of the box.

type example class
String 'hello' React.PropTypes.string
Number 10, 0.1 React.PropTypes.number
Boolean true / false React.PropTypes.bool
Function const say => (msg) => console.log("Hello world") React.PropTypes.func
Symbol Symbol("msg") React.PropTypes.symbol
Object {name: 'Ari'} React.PropTypes.object
Anything 'whatever', 10, {}

It's possible to tell React we want it to pass through anything that can be rendered by using React.PropTypes.node:

type example class
A rendererable 10, 'hello' React.PropTypes.node

Clock.propTypes = {
  title: React.PropTypes.string,
  count: React.PropTypes.number,
  isOn: React.PropTypes.bool,
  onDisplay: React.PropTypes.func,
  symbol: React.PropTypes.symbol,
  user: React.PropTypes.object,

  name: React.PropTypes.node
}

We've already looked at how to communicate from a parent component to a child component using props. We can communicate from a child component to a parent component using a function. We'll use this pattern quite often when we want to manipulate a parent component from a child.

Collection types

We can pass through iterable collections in our props. We've already seen how we can do this when we passed through an array with our activities. To declare a component's proptype as an array, we can use the React.PropTypes.array annotation.

We can also require that an array holds only objects of a certain type using React.PropTypes.arrayOf([]).

type example class
Array [] React.PropTypes.array
Array of numbers [1, 2, 3] React.PropTypes.arrayOf([type])
Enum ['Red', 'Blue'] React.PropTypes.oneOf([arr])

It's possible to describe an object that can be one of a few different types as well using React.PropTypes.oneOfType([types]).


Clock.propTypes = {
  counts: React.PropTypes.array,
  users: React.PropTypes.arrayOf(React.PropTypes.object),
  alarmColor: React.PropTypes.oneOf(['red', 'blue']),
  description: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.instanceOf(Title)
    ]),
}

Object types

It's possible to define types that need to be of a certain shape or instance of a certain class.

type example class
Object {name: 'Ari'} React.PropTypes.object
Number object {count: 42} React.PropTypes.objectOf()
Instance new Message() React.PropTypes.objectOf()
Object shape {name: 'Ari'} React.PropTypes.shape()

Clock.propTypes = {
  basicObject: React.PropTypes.object,

  numbers: React.PropTypes
    .objectOf(React.PropTypes.numbers),

  messages: React.PropTypes
    .instanceOf(Message),

  contactList: React.PropTypes.shape({
    name: React.PropTypes.string,
    phone: React.PropTypes.string,
  })
}

React types

We can also pass through React elements from a parent to a child. This is incredibly useful for building templates and providing customization with the templates.

type example class
Element <Title /> React.PropTypes.element

Clock.propTypes = {
  displayEle: React.PropTypes.element
}

When we use element, React expects that we'll be able to accept a single child component. That is, we won't be able to pass multiple elements.

// Invalid for elements
<Clock displayElement={
  <div>Name</div>
  <div>Age</div>
}></Clock>
// Valid
<Clock displayElement={
  <div>
    <div>Name</div>
    <div>Age</div>
  </div>
}></Clock>

Requiring types

It's possible to require a prop to be passed to a component by appending any of the proptype descriptions with .isRequired:

Clock.propTypes = {
  title: React.PropTypes.name.isRequired,
}

Setting a prop as required is very useful for times when the component is dependent upon a prop to be passed in by it's parent component and won't work without it.

Custom types

Finally, it's also possible to pass a function to define custom types. We can do this for a single prop or to validate arrays. The one requirement for the custom function is that if the validation does not pass, it expects we'll return an Error object:

type example class
Custom 'something_crazy' function(props, propName, componentName) {}
CustomArray ['something', 'crazy'] React.PropTypes.arrayOf(function(props, propName, componentName) {})
Clock.propTypes = {
  userWithName: (props, propName, componentName) => {
    if (!props[propName] || !props[propName].name) {
      return new Error(
        `Invalid ${propName}: No name property defined
          for component ${componentName}`
      )
    }
  },

  usersWithNames: React.PropTypes
    .arrayOf((props, propName, componentName) => {
      if (!props[propName] || !props[propName].name) {
      return new Error(
        `Invalid ${propName}: No name property defined
          for component ${componentName}`
      )
    }
    })
}

Default props

Sometimes we want to be able to set a default value for a prop. For instance, our <Header /> component, we built yesterday might not require a title to be passed. If it's not, we'll still want a title to be rendered, so we can define a common title instead by setting it's default prop value.

To set a default prop value, we can use the defaultProps object key on the component.

Header.defaultProps = {
  title: 'Github activity'
}

When using the React.createClass() form, we can define an object key called getDefaultProps() which is expected to return an object with the default values of props.

React.createClass({
  getDefaultProps: () => ({
    name: 'Github activity'
  })
})

Phew, today we went through a lot of documentation. It's always a good idea to build our resuable components using the propTypes and defaultProps attributes of components. Not only will it make it easier to communicate across developers, it'll be much easier when we return to our components after leaving them for a few days. Next, we'll get back to code and start integrating some style into our components.


Ari Lerner

Hi, I'm Ari. I'm an author of Fullstack React and ng-book and I've been teaching Web Development for a long time. I like to speak at conferences and eat spicy food. I technically got paid while I traveled the country as a professional comedian, but have come to terms with the fact that I am not funny.

Connect with Ari on Twitter at @auser.