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!

Packaging and PropTypes

Edit this page on Github

We're looking at how to make reusable React components today so we can 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 and packaging 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 prop-types 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
}

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:

First, we'll need to import the PropTypes object from the prop-types package using the import keyword again:

import PropTypes from 'prop-types'

You can also use the PropTypes object directly in your browser by adding the following script tag in your page

<script src="https://unpkg.com/[email protected]/prop-types.min.js"></script>
import PropTypes from 'prop-types'

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

Header.propTypes = {
  title: PropTypes.string
}

React has a lot of types to choose from, exported on the 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' PropTypes.string
Number 10, 0.1 PropTypes.number
Boolean true / false PropTypes.bool
Function const say => (msg) => console.log("Hello world") PropTypes.func
Symbol Symbol("msg") PropTypes.symbol
Object {name: 'Ari'} PropTypes.object
Anything 'whatever', 10, {}

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

type example class
A rendererable 10, 'hello' PropTypes.node
Clock.propTypes = {
  title: PropTypes.string,
  count: PropTypes.number,
  isOn: PropTypes.bool,
  onDisplay: PropTypes.func,
  symbol: PropTypes.symbol,
  user: PropTypes.object,

  name: 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 PropTypes.array annotation.

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

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

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

Clock.propTypes = {
  counts: PropTypes.array,
  users: PropTypes.arrayOf(PropTypes.object),
  alarmColor: PropTypes.oneOf(['red', 'blue']),
  description: PropTypes.oneOfType([
      PropTypes.string,
      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'} PropTypes.object
Number object {count: 42} PropTypes.objectOf()
Instance new Message() PropTypes.objectOf()
Object shape {name: 'Ari'} PropTypes.shape()
Clock.propTypes = {
  basicObject: PropTypes.object,

  numbers: PropTypes
    .objectOf(PropTypes.numbers),

  messages: PropTypes
    .instanceOf(Message),

  contactList: PropTypes.shape({
    name: PropTypes.string,
    phone: 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 /> PropTypes.element
Clock.propTypes = {
  displayEle: 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: 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'] PropTypes.arrayOf(function(props, propName, componentName) {})
UserLink.propTypes = {
  userWithName: (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'
}

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.

Learn React the right way

The up-to-date, in-depth, complete guide to React and friends.

Download the first chapter

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.