Type Checking React with PropTypes

Photo by Tom Chen on Unsplash

Type Checking React with PropTypes

PropTypes Are Just A Poor Mans Typescript Aren't They?

With the increasing popularity of Typescript, you might be wondering. Why would I need another way to check types? But React PropTypes are different in one distinct way.

PropTypes check the types of your props at run time. Why is that useful? Well, most of your data comes from an API so typescript never sees what type it is.

Yes, your code seems to work and yes doing things at run time will slow you down a little. But propTypes won't be checked in production and you can even use babel to strip them out of the production code so they won't even ship to production. Besides, what is the most important but often-overlooked reason to check your types? Performance!

You're Checking Types To Improve Performance

As your browser parses your code during run time compiling it optimises all your code into code that runs a lot faster. If that function gets a number as a prop your browser can optimise it... but if in a future scenario you get a string... well your browser is going to ditch the optimised function and all your code will be slower. And most examples aren't that simple, maybe you pass the exact same object structure but on one occasion it's missing an attribute, that'll stop your browser from running optimise code and slow the application down. PropTypes warn you about these when they happen, at run time.

You can use both Typescript and PropTypes and there are some Babel plugins that'll compile your Typescript to PropTypes for you.

A Simple React Component

Let's get a simple react modal component.

const Modal = ({ heading, handleClose, show, children }) => 
    <div style={{ display: show ? 'block' : 'none' }}>
      <section className="modal">
        <h3>{heading}</h3>
        {children}
        <button type="button" onClick={handleClose}>
          Close
        </button>
      </section>
    </div>

export default Modal;

Now we can see that this accepts three props, a string, a simple boolean, a function and some children which will need to be a jsx element.

let's import propTypes and set up our basic prop types

Adding Prop Types

import PropTypes from 'prop-types';

const Modal = ({ heading, handleClose, show, children }) => 
    <div style={{ display: show ? 'block' : 'none' }}>
      <section className="modal">
        <h3>{heading}</h3>
        {children}
        <button type="button" onClick={handleClose}>
          Close
        </button>
      </section>
    </div>

Modal.PropTypes = {
  title: PropTypes.string,
  show: PropTypes.bool,
  handleClose: PropTypes.func,
  children: PropTypes.element
}

export default Modal;

element will require the children to be a single element.

We could also use the propTypes, number if we have any numbers. Sometimes we're going to pass an array, for which we'll need to specify what it's an array of:
PropTypes.arrayOf(PropTypes.number) and some times we'll have a complex object where we'll need to explain the whole structure:
PropTypes.shape({ attributeName1: PropTypes.string, attrinuteName2: PropTypes.number }). and sometimes we want a property to be optional

Default Values

import PropTypes from 'prop-types';

const Modal = ({ heading, handleClose, show, children }) => 
    <div style={{ display: show ? 'block' : 'none' }}>
      <section className="modal">
        <h3>{heading}</h3>
        {children}
        <button type="button" onClick={handleClose}>
          Close
        </button>
      </section>
    </div>

Modal. defaultProps = {
  title: "settings",
  show: false
}

Modal.PropTypes = {
  title: PropTypes.string,
  show: PropTypes.bool,
  handleClose: PropTypes.func,
  children: PropTypes.element
}

export default Modal;

for the child element and the function, we don't have a default, but we could set them to required to ensure the properties are always set.

Setting required properties

import PropTypes from 'prop-types';

const Modal = ({ heading, handleClose, show, children }) => 
    <div style={{ display: show ? 'block' : 'none' }}>
      <section className="modal">
        <h3>{heading}</h3>
        {children}
        <button type="button" onClick={handleClose}>
          Close
        </button>
      </section>
    </div>

Modal.defaultProps = {
  title: "settings",
  show: false
}

Modal.PropTypes = {
  title: PropTypes.string,
  show: PropTypes.bool,
  handleClose: PropTypes.func.isRequired,
  children: PropTypes.element.isRequired
}

export default Modal;

by simply adding .isRequired we are now forcing these properties to show.

conclusion

Setting PropTypes in a react component has a very low bar for entry with such simple syntax. It runs at run time so we can see if we have type issues when we have real data running in our application. But as you can see from the code samples, it forces us to be declarative with our React components, explicitly stating what we expect it to receive and if it's required. It does all this in a way any developer opening the file can easily see the structure separate from the code for the component. A very useful extra bonus.