DEV Community

loading...
Cover image for How to forget about type errors in your React props with PropTypes

How to forget about type errors in your React props with PropTypes

asayerio_techblog profile image Asayer Tech Blog Originally published at blog.asayer.io ・8 min read

by Author Nwose Lotanna Victor

Do you write or maintain code using React? Then you must be familiar with constant debugging that happens as you build and after you build an app. Do you want to reduce debug time drastically, then this post is for you. In this article, we will be looking at how to making your debugging process faster with prop types in React, what they are, how they are related to TypeScript, and how they are used with practical examples.

What are prop types

React prop type is a library supported by React that lets you define types for props in your components. Prototypes are a simple way of validating or ensuring that the data we pass from one component to another or from one element to another is the exact type of data we intend on passing. In React, one of the ways to pass data is through properties and so as you will see later in this post, it is important to check those types.
Since React is a JavaScript framework and we know that it is dynamically typed, so property types in JavaScript are determined at runtime. Type checking is something TypeScript as a language is well known for and so you might wonder how they relate with React prop types.

As your application gets bigger and bigger the chances of having more bugs increase, knowing that you can catch a lot of them easily through type-checking is a great thing. With Proptypes, whether or not you are writing, reading, or maintaining a codebase the components will always be predictable for you.

How it relates to TypeScript

TypeScript is a statically typed version of JavaScript which solves the dynamic typing issues around knowing or enforcing types for not just properties but for almost everything else in JavaScript. However, with TypeScript, the types are checked at compile time which means that as you write your code you get the warnings in real-time. With Proptypes, these checks happen at runtime and so are focused on component interaction and debugging. Using prop types in React gives you a bit of the TypeScript experience at runtime without having to use TypeScript.

The problem

The interesting use case here is how you can pass in any type of data and it compiles without any warnings in React. This can be very costly as it becomes tough to notice while debugging and most of the time these are the kinds of things that the user of the application first notices.

What we will be building

How does your component look without prop types? Let us find out with this demo below

demo gif

This is a simple car list displayed in a card-like form, if you followed this post from the start you must have created the “propstest” project which we will be using throughout this tutorial.
Navigate to a folder of your choice and open that in VS Code, run the command below in the terminal:

npx create-react-app propstest
Enter fullscreen mode Exit fullscreen mode

Inside the source folder create a components folder and then a functional component file, call it Car.js and copy this code block below inside it:

import React from "react"
const Car = (prop) => {
    return ( <div className = "container responsive" >
        <div className = "car" >
          <h2 > This {prop.brand} {prop.model
          } < /h2> 
          <h4 > has a millage of {prop.milage} < /h4> 
          <h4 > It is an electric vehicle - {prop.isElectric} < /h4> 
          <ul > {
              prop.owners.map((owner, key) => {
                  return <li key = {key} > {owner} < /li>;
              })
          } 
          </ul> 
          </div> 
        </div>
    );
};
export default Car;
Enter fullscreen mode Exit fullscreen mode

This is a simple functional component that renders and export Car template with some headers and a list. Inside the source folder, you can see the app component (App.js) file, copy the code block below into the app.js file:

import './App.css';
import Car from './Components/Car'

function App() {
    return ( <div className = "App" >
        <Car brand = "Tesla"
          model = "CyberTruck"
          milage = {5000}
          isElectric = {"true"}
          owners = {["Lotanna"]}
        /> 
        <Car brand = "Ford"
          model = "Explorer"
          milage = {8000}
          isElectric = {"false"}
          owners = {["Runor", "Elijah"]}
        /> 
        <Car brand = "Benz"
          model = "CLA250"
          milage = {7500}
          isElectric = {"false"}
          owners = {["Gilbert", "Herbert", "Frank", "Mazior"]}
        /> 
        <Car brand = "Toyota"
          model = "Camry"
          milage = {3000}
          isElectric = {"false"}
          owners = {["Ebuka", "Ikenna", "Ekene"]}
        /> 
        </div >
    );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

Here we are bringing in the defined templates and telling React to render it four times with our own pre-defined properties. These properties include the car brand, the model, mileage, and the owners of the car. When you run the dev server command:

npm start
Enter fullscreen mode Exit fullscreen mode

You will see information all scattered, adding a little CSS rule to make it look presentable, navigate to the app.css file and copy these styles below:

.App {
    text-align: center;
}

.container {
    position: relative;
}

ul {
    list-style-type: none;
    text-align: center;
}

.car {
    top: 50%;
    text-align: center;
    border-radius: 25px;
    border: 2px solid #73AD21;
    padding: 20px;
    width: 600px;
    height: 250px;
    margin-left: 25%;
    margin-bottom: 15px;
}
Enter fullscreen mode Exit fullscreen mode

Running the dev server command again,
you will notice in your browser at http: //localhost:3000/ that it looks exactly like the demo gif we have above.
Now we can go into the app component file and change the Tesla string to a number,
say 5000 and the milage to Rabbit,
the application still runs fine without an error or warning at all.

Things like this can be very hard to catch during a debugging session, it can happen through human error, direct user input or data from another component or API especially if you are not the author of the codebase. We can go even further to change the owner's side from an array containing “lotanna” to just a string.

The app crashes on compile, but not because of the type, only because we have a map function for the owners, and the map only works on a list (in our case, an array) ## Solution: prop types A great solution to this problem is to define the types for every prop you want to use. The Syntax of prop types looks like this in a functional component:

import PropTypes from 'prop-types'

function HelloWorldComponent( {name}) {
    return (<div>Hello, {name} </div>)
}

HelloWorldComponent.propTypes = {
    name: PropTypes.string
}

export default HelloWorldComponent
Enter fullscreen mode Exit fullscreen mode

Let us see how it works in our app, start by installing the prop types package inside your project with this command:

npm install proptypes
Enter fullscreen mode Exit fullscreen mode

Inside the component you want to define the types, import the installed prop types, in our case inside the Car.js file:

import React from "react"
import PropTypes from "prop-types"
const Car = (prop) => {
    return ( <
        div className = "container responsive" >
        <div className = "car" >
          <h2> This {prop.brand } { prop.model } < /h2> 
          <h4> has a millage of { prop.milage } < /h4> 
          <h4 > It is an electric vehicle - { prop.isElectric } </h4> 
          <ul> {
              prop.owners.map((owner, key) => {
                  return <li key = {key} > {owner} < /li>;
              })
          } 
          </ul> 
          </div> 
        </div>
    );
};
Car.propTypes = {
    brand: PropTypes.string,
    model: PropTypes.string,
    milage: PropTypes.number,
    isElectric: PropTypes.string,
    owners: PropTypes.arrayOf(PropTypes.node)
}
export default Car;
Enter fullscreen mode Exit fullscreen mode

Notice the prop types being defined before the export of the component, that is very important because if you export before defining the prop types, it would not work in the dev server.

This reminds me so much of form validator libraries, they are basically structured like this as well. We added a few prop types in this Car component and some of them are:

  • Proptype.string: This defines the type of prop required here is of type string.
  • Proptype.number: This defines the needed type of prop here to be type number.
  • Proptype.arrayOf: This defines the type of prop required here to be an array type, inside which you can now specify exactly the type of the children.
  • Proptype.node: This defines the type of anything that can be rendered in the UI. That includes numbers, strings, elements, an array, or even fragments.

There are other types you can read about in the documentation here.

Testing the prop types

Now if you run the application in your dev server you will see that everything runs perfectly however when you try to change Tesla to a number the application still runs but this time it shows a warning in the console.

These warnings are very descriptive that once you read them you know exactly what has gone wrong and what to fix.

More use cases: OneOf example

It gets even more interesting, with oneOf you can specify the exact value you want from your props so no matter where the data source is, if it does not match your specifications you would get the warning. Let us define exact values in the Car component like this:

import React from "react"
import PropTypes from "prop-types"
const Car = (prop) => {
    return ( <div className = "container responsive" >
        <div className = "car" >
          <h2 > This { prop.brand } { prop.model } < /h2> 
          <h4 > has a millage of { prop.milage } < /h4> 
          <h4 > It is an electric vehicle - { prop.isElectric } < /h4> 
          <ul> { prop.owners.map((owner, key) => {
                  return <li key = { key } > { owner } < /li>;
              })
          } 
          </ul> 
          </div> 
        </div>
    );
};
Car.propTypes = {
    brand: PropTypes.oneOf(['Ford', 'Benz', 'Toyota']),
    model: PropTypes.string,
    milage: PropTypes.number,
    isElectric: PropTypes.string,
    owners: PropTypes.arrayOf(PropTypes.node)
}
export default Car;
Enter fullscreen mode Exit fullscreen mode

You can see we specified the exact values we needed and I purposely omitted Tesla and so when we run the app again, here is the warning we get.

Prop types in class components

Prop types also work well in React class components, let us quickly create one class component. Open the components folder and create a new component file, call it Sample.js , and inside it copy the code block below:

import React from "react"
import PropTypes from 'prop-types';
class Greeting extends React.Component {
    render() {
        return ( <h1 > Hello, { this.props.brand } < /h1>
        );
    }
}
Greeting.propTypes = {
    brand: PropTypes.string
};
export default Greeting;
Enter fullscreen mode Exit fullscreen mode

Inside the app component file (App.js) import Greeting and add the corresponding template like this:

import './App.css';
import Car from './Components/Car'
import Greeting from './Components/Sample'

function App() {
    return ( <
        div className = "App" >
        <Greeting brand = "Reader" />
        <Car brand = { 4555 }
          model = "CyberTruck"
          milage = { 5000 }
          isElectric = { "true" }
          owners = { ["Lotanna"] }
        /> 
        <Car brand = "Ford"
          model = "Explorer"
          milage = { 8000 }
          isElectric = { "false" }
          owners = { ["Runor", "Elijah", 3] }
        /> 
        <Car brand = "Benz"
          model = "CLA250"
          milage = {7500}
          isElectric = { "false" }
          owners = { ["Gilbert", "Herbert", "Frank", "Mazior"] }
        /> 
        <Car brand = "Toyota"
          model = "Camry"
          milage = { 3000 }
          isElectric = { "false" }
          owners = { ["Ebuka", "Ikenna", "Ekene"] }
        /> 
        < /div >
    );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

When you run it in the dev server you can see it shows up like this:

Observability for Production React Apps

Debugging React apps in production may be challenging and time consuming. Asayer is a frontend monitoring tool that replays everything your users do and shows how your app behaves and renders for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder.

Asayer Frontend Monitoring

Asayer helps to quickly get to the root cause by reproducing issues as if they happened in your own browser. It also monitors your frontend performance by capturing key metrics such as page load time, memory consumption and slow network requests as well as Redux actions/state.

Happy debugging, for modern frontend teams - Start monitoring your web app for free.

Wrapping up

In this tutorial, we have taken a look at prop types in React, why they are important and how they can be relatable to TypeScript. We also took a look at how they are used with various examples and a working demo. I hope you would adopt prop types going forward to make debugging and maintaining your code easier. Happy hacking!

Discussion (0)

pic
Editor guide