Avoid repeating yourself by centralizing PropTypes

There are three popular ways to handle types in React: PropTypes, TypeScript and Flow. This post is about PropTypes, which are currently the most popular.

Since PropTypes provide type warnings at runtime, it’s helpful to be as specific as possible.

  • Component accepts an object? Declare the object’s shape.
  • Prop only accepts a specific list of values? Use oneOf.
  • Array should contain numbers? Use arrayOf.
  • You can even declare your own types. AirBnB offers many additional PropTypes.

Here’s a PropType example:

UserDetails.propTypes = {
 user: PropTypes.shape({
   id: PropTypes.number.isRequired,
   firstName: PropTypes.string.isRequired,
   lastName: PropTypes.string.isRequired,
   role: PropTypes.oneOf(['user','admin'])
};

In real apps with large objects, this quickly leads to a lot of code. That’s a problem, because in React, you’ll often pass the same object to multiple components. Repeating these details in multiple component files breaks the DRY principle (don’t repeat yourself). Repeating yourself creates a maintenance problem.

The solution? Centralize your PropTypes.

Here’s How to Centralize PropTypes

I prefer centralizing PropTypes in /types/index.js.

I’m using named imports on line 2 to shorten the declarations. ?

And here’s how I use the PropType I declared above:

// types/index.js
import { shape, number, string, oneOf } from 'prop-types';

export const userType = shape({
  id: number,
  firstName: string.isRequired,
  lastName: string.isRequired,
  company: string,
  role: oneOf(['user', 'author']),
  address: shape({
    id: number.isRequired,
    street: string.isRequired,
    street2: string,
    city: string.isRequired,
    state: string.isRequired,
    postal: number.isRequired
  });
});

I use a named import to get a reference to the exported PropType declaration on line 2. And I put it to use on line 13.

Benefits:

  1. The centralized PropType radically simplifies the component’s PropType declaration. Line 13 just references the centralized PropType, so it’s easy to read.
  2. The centralized type only declares the shape, so you can still mark the prop as required as needed.
  3. No more copy/paste. If the object shape changes later, you have a single place to update. ?

Here’s a working example on CodeSandbox.

Extra Credit: Generate Your PropTypes

Finally, consider writing some custom code to generate your PropType declarations from your server-side code. For example, if your API is written using a strongly typed language like C# or Java, consider generating your PropType declarations as part of your server-side API build process by reading the shape of your server-side classes. This way you don’t have to worry about keeping your client-side PropTypes and your server-side API code in sync. ?

Side-note: If you know of a project that does this for any server-side languages, please reply in the comments and I’ll add a link here.

Edit: You can convert JSON into PropTypes using transform.now.sh. ?

Summary

  1. Declare your PropTypes as explicitly as possible, so you know when you’ve made a mistake.
  2. Centralize your PropTypes to avoid repeating yourself.
  3. If you’re working in a strongly typed language on the server, consider generating your PropTypes by reading your server-side code. This assures your PropTypes match your server-side types.

Looking for More on React? ⚛️

I’ve authored multiple React and JavaScript courses on Pluralsight (free trial).

Cory House is the author of multiple courses on JavaScript, React, clean code, .NET, and more on Pluralsight. He is principal consultant at reactjsconsulting.com, a Software Architect at VinSolutions, a Microsoft MVP, and trains software developers internationally on software practices like front-end development and clean coding. Cory tweets about JavaScript and front-end development on Twitter as @housecor.