Appendix A: PropTypes

PropTypes are a way to validate the values that are passed in through our props. Well-defined interfaces provide us with a layer of safety at the run time of our apps. They also provide a layer of documentation to the consumer of our components.

We define PropTypes by passing them as an option to createClass():

const Component = React.createClass({
  propTypes: {
    // propType definitions go here
  },
  render: function() {}
});

Classical style propTypes

Defining propTypes in a class-based component using ES6 syntax is slightly different as it needs to be defined as a class method on the component. For example:

class Component extends React.Component {
  render() {}
}
Component.propTypes = {/* definition goes here*/};

The key of the propTypes object defines the name of the prop we are validating, while the value is the types defined by the React.PropTypes object (which we discuss below) or a custom one through a custom function.

The React.PropTypes object exports a lot of validators that cover the most of the cases we'll encounter. For the not-so common cases, React allows us to define our own PropType validators as well.

Validators

The React.PropTypes object contains a list of common validators (but we can also define our own. More on that later).

When a prop is passed in with an invalid type or fails the prop type, a warning is passed into the JavaScript console. These warnings will only be shown in development mode, so if we accidentally deploy our app into production with an improper use of a component, our users won't see the warning.

The built in validators are:

string

To define a prop as a string, we can use React.PropTypes.string.

const Component = React.createClass({
  propTypes: {
    name: React.PropTypes.string
  },
  // ...
})

To pass a string as a prop we can either just pass the string itself as an attribute or use the {} braces to define a string variable. These are all functionally equivalent:

<Component name={"Ari"} />
<Component name="Ari" />

number

To specify a prop should be a number, we can use React.PropTypes.number.

const Component = React.createClass({
  propTypes: {
    totalCount: React.PropTypes.number
  },
  // ...
})

Passing in a number, we must pass it as a JavaScript value or the value of a variable using braces:

var x = 20;

<Component totalCount={20} />
<Component totalCount={x} />

boolean

To specify a prop should be a boolean (true or false), we can use React.PropTypes.bool.

const Component = React.createClass({
  propTypes: {
    on: React.PropTypes.bool
  },
  // ...
})

To use a boolean in a JSX expression, we can pass it as a JavaScript value.

var isOn = true;

<Component isOn={true} />
<Component isOn={false} />
<Component on={isOn} />

A trick for using booleans to show or hide content is to use the && expression.

For instance, inside our Component.render() function, if we want to show content only when the on proptype is true, we can do so like:

render: function() {
  return (
    <div>
      {this.props.on && <p>This component is on</p>}
    </div>
  )
}

function

We can pass a function as a prop as well. To define a prop to be a function, we can use React.PropTypes.func. Often times when we write a Form component, we'll pass a function as a prop to be called when the form is submitted (i.e. onSubmit()). It's common to define a prop as a required function on a component.

const Component = React.createClass({
  propTypes: {
    onComplete: React.PropTypes.func
  },
  // ...
})

We can pass a function as a prop by using the JavaScript expression syntax, like so:

const x = function(name) {};
const fn = value => alert("Value: " + value);

<Component onComplete={x} />
<Component onComplete={fn} />

object

We can require a prop should be a JavaScript object through the React.PropTypes.object:

const Component = React.createClass({
  propTypes: {
    user: React.PropTypes.object
  },
  // ...
})

Sending an object through as a prop, we'll need to use the JavaScript expression {} syntax:

const user = {
  name: 'Ari'
}

<Component user={user} />
<Component user={ {name: 'Anthony'} } />

object shape

React allows us to define the shape of an object we expect to receive by using React.PropTypes.shape(). The React.PropTypes.shape() function accepts an object with a list of key-value pairs that dictate the keys an object is expected to have as well as the value type:

const Component = React.createClass({
  propTypes: {
    user: React.PropTypes.shape({
      name: React.PropTypes.string,
      friends: React.PropTypes.arrayOf(React.PropTypes.object),
      age: React.PropTypes.number
    })
  },
  // ...
})

multiple types

Sometimes we don't know in advance what kind a particular prop will be, but we can accept one or other type. React gives us the propTypes of oneOf() and oneOfType() for these situations.

Using oneOf() requires that the propType be a discrete value of values, for instance to require a component to specify a log level value:

const Component = React.createClass({
  propTypes: {
    level: React.PropTypes.oneOf(['debug', 'info', 'warning', 'error'])
  },
  // ...
})

Using oneOfType() says that a prop can be one of any number of types. For instance, a phone number may either be passed to a component as a string or an integer:

const Component = React.createClass({
  propTypes: {
    phoneNumber: React.PropTypes.oneOfType([
      React.PropTypes.number,
      React.PropTypes.string
    ])
  },
  // ...
})

instanceOf

We can dictate that a component must be an instance of a JavaScript class using React.PropTypes.instanceOf() as the value of the propType:

const Component = React.createClass({
  propTypes: {
    user: React.PropTypes.instanceOf(User)
  },
  // ...
})

We'll use the JavaScript expression syntax to pass in a particular prop.

const User = function(name) {
  this.name = name;
  return this;
};

const ari = new User('Ari');

<Component user={ari} />

array

On occasion we'll want to send in an array as a prop. To set an array, we'll use the React.PropTypes.array as the value.

const Component = React.createClass({
  propTypes: {
    authors: React.PropTypes.array
  },
  // ...
})

Sending an object through as a prop, we'll need to use the JavaScript expression {} syntax:

const users = [
  {name: 'Ari'}
  {name: 'Anthony'}
];


<Component authors={[{name: 'Anthony'}]} />
<Component authors={users} />

array of type

React allows us to dictate the type of values each member of an array should be using React.PropTypes.arrayOf().

const Component = React.createClass({
  propTypes: {
    authors: React.PropTypes.arrayOf(React.PropTypes.object)
  },
  // ...
})

We'll use the JavaScript expression syntax {} to pass in an array:

const users = [
  {name: 'Ari'}
  {name: 'Anthony'}
];


<Component authors={[{name: 'Anthony'}]} />
<Component authors={users} />

node

We can also pass anything that can be rendered, such as numbers, string, DOM elements, arrays, or fragments that contain them using the React.PropTypes.node.

const Component = React.createClass({
  propTypes: {
    icon: React.PropTypes.node
  },
  // ...
})

Passing a node as a prop is straightforward as well. Passing a node as a value is often useful when requiring a component to have children or setting a custom element. For instance, if we want to allow our user to pass in either the name of an icon or a custom component, we can use the node propType.

const icon = <FontAwesomeIcon name="user" />


<Component icon={icon} />
<Component icon={"fa fa-cog"} />

element

React's flexibility allows us to pass another React element in as a prop as well by using the React.PropTypes.element:

const Component = React.createClass({
  propTypes: {
    customHeader: React.PropTypes.element
  },
  // ...
})

We can build our components so that the interface they allow our users to specify a custom component. For instance, we might have a <List /> component who's responsibility is to output a list of elements. Without custom components, we would have to build a separate <List /> React component for each type of list we want to render (which might be appropriate, depending on the behavior of the element). By passing a component type, we can reuse the <List /> component.

For instance, a list component might look like:

const List = React.createClass({
  propTypes: {
    listComponent: PropTypes.element,
    list: PropTypes.array
  },
  renderListItem: function(item, i) {
    const Component = this.props.listComponent || "li";
    return React.createElement(Component, this.props, item)
  },
  render: function() {
    const list = this.props.list;
    return (
      <ul>
        {list.map(this.renderListItem)}
      </ul>
    )
  }
});

We can use this list component with or without specifying a custom component:

const Item = React.createClass({
  render: function() {
    return (
      <div>{this.props.children}</div>
    )
  }
})

<List list={[1, 2, 3]} />
<List list={[1, 2, 3]} listComponent={Item} />

any type

React also allows us to specify that a prop must be present, regardless of it's type. We can do this by using the React.PropTypes.any validator.

const Component = React.createClass({
  propTypes: {
    mustBePresent: React.PropTypes.any
  },
  // ...
})

Optional & required props

All props are considered optional unless otherwise specified. To require a prop be passed to a component and validated, we can append every propType validation with .isRequired.

For instance, if we must have a function to get called after some action when a Loading component has completed, we can specify it like this:

const Loading = React.createClass({
  propTypes: {
    // Optional props:
    onStart: React.PropTypes.func,
    // Required props:
    onComplete: React.PropTypes.func.isRequired,
    name: React.PropTypes.string.isRequired
  },
  // ...
})

custom validator

React allows us to specify a custom validation function for all of the other situations where the default validation functions don't cover it. In order to run a write a custom validation we'll specify a function that accepts 3 arguments:

  1. The props passed to the component
  2. The propName being validated
  3. The componentName we're validating against

If our validation passes, we can run through the function and return anything. The validation function will only fail if an Error object is raised (i.e. new Error()).

For instance, if we have a loader that accepts validated users, we can run a custom function against the prop.

const Component = React.createClass({
  propTypes: {
    user: function(props, propName, componentName) {
      const user = props[propName];
      if (!user.isValid()) {
        return new Error('Invalid user');
      }
    }
  },
  // ...
})