React Note: Javascript Typechecking

About React Note

I'm currently working on an application using React.js. I've been meaning to learn this framework for a long time. So, it's a great opportunity to learn more and more about React while also making some real-world coins. React Note will be a series of writing about React software development. Tips, examples, best practices, anything that I find useful.

Enjoy!

Typechecking in React

You can use React.js with either Javascript or Typescript. If you decided to use Javascript, you'll know that Javascript lacks variable typechecking that Typescript has. However, you can utilize one of React's convenient typechecking system, that is PropTypes.

Here's the overview of what we'll be doing here:

  1. Preparation
  2. Import PropTypes
  3. Declare propTypes property
  4. Define property type
  5. Define required/optional and default value
  6. Define property's value range
  7. Advanced typechecking

1. Preparation

Throughout this guide, we will be using a React component named Product defined below. We will add typechecking to its properties: name and price. We will also add more properties as we go.

Important Note: To see typechecking in action, you need to open your browser's Inspection Tool. If typechecking sees an error, you'll see it in the console.

// Initial code.
import React from "react";

class Product extends React.Component 
{
    render() 
    {
        return (
            <div>
                <h1>{this.props.name}</h1>
                <p>Rp {this.props.price}</p>
            </div>
        );
    }
}

export default Product;

2. Import PropTypes

Import prop-types to top of the file.

import React from "react";
// Add this new line.
import PropTypes from "prop-types";

3. Declare propTypes property

Declare propTypes property for the component before the export statement.

// ... rest of the code

// Add this new line before the export statement.
Product.propTypes = 
{
    // Your typechecking will be written here.
}

export default Product;

4. Define property type

Based on the type of property that will be typechecked, follow the questions below:  

4.1. Is the property one of the following types: number, string, boolean, function, or symbol?  

If yes, use one of the following:  

Product.propTypes = 
{
  name: PropTypes.string,     // string
  price: PropTypes.number,    // number

  isEmpty: PropTypes.bool,    // boolean
  onClick: PropTypes.func,    // function
  symbol: PropTypes.symbol,   // symbol
}

4.2. Is the property an array type?  

If yes, you have two options: PropTypes.array and PropTypes.arrayOf().   

Does the array's elements all have the same type?  

  • If yes, use PropTypes.arrayOf(). Example:
    Product.propTypes = 
    {
      // sizes could only have type of string.
      // e.g., ["small", "medium", "large"]
      sizes: PropTypes.arrayOf(PropTypes.string),
    }​

  • If not, use PropTypes.array. Example:
    Product.propTypes = 
    {
      // sizes could have elements of different types.
      // e.g., [20, "medium", 30]
      sizes: PropTypes.array,
    }​

4.3. Is the property an object type?  

If yes, you have three options: PropTypes.shape(), PropTypes.objectOf(), and PropTypes.object.   

Does the object have a specific shape?  

  • If yes, use PropTypes.shape(). Example:
    Product.propTypes =
    {
      // seller's property values must be an object with name and age property.
      // e.g., seller = { name: "Majid", age: 23 }
      seller: PropTypes.shape({
        name: PropTypes.string,
        age: PropTypes.number,
      }),
    }

Does the object's property values all have the same type?  

  • If yes, use PropTypes.objectOf(). Example:
    Product.propTypes =
    {
      // seller's property values must be string
      // e.g., seller = { name: "Majid" }
      // or seller = { name: "Majid", age: "18-30" }
      seller: PropTypes.objectOf(PropTypes.string),
    }
  • If not, use PropTypes.object. Example:
    Product.propTypes =
    {
      // seller could be any object.
      // e.g., seller = { name: "Majid" }
      // or seller = { name: "Majid", age: 23 }
      seller: PropTypes.object,
    }​

4.4. Is the property an instance of a class?  

If yes, use PropTypes.instanceOf(). Example:

Product.propTypes =
{
  // seller must be an instance of User class.
  // e.g., seller = new User()
  seller: PropTypes.instanceOf(User),
}

4.5. Is the property a React element?  

If yes, use PropTypes.element. Example:

Product.propTypes = 
{
  // seller must be a React element
  // e.g., seller = <Seller />
  seller: PropTypes.element,
}

4.6. Is the property a node? 

A node is defined as anything that can be rendered: numbers, strings, elements or an array (or fragments) containing these types.  

If yes, use PropTypes.node. Example:

Product.propTypes = 
{
  // seller must be a node.
  // e.g., seller = "Majid"
  // or seller = <Seller />
  seller: PropTypes.node,
}

4.7. Could the property have any type?  

If yes, PropTypes.any. Example:

Product.propTypes = 
{
  // seller can be anything.
  // e.g., seller = "Majid"
  // or seller = { name: "Majid", age: 23 }
  seller: PropTypes.any,
}

4.8. Could the property have many types?  

If yes, PropTypes.oneOfType(). Example:

Product.propTypes = 
{
  // seller can be any of the following: a string, a User class instance, or a React element.
  // e.g., seller = "Majid"
  // or seller = new User()
  // or sellet = <Seller />
  seller: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.instanceOf(User),
    PropTypes.element,
  ]),
}

5. Define required/optional and a default value

Is the property required or optional?

  • If required, chain isRequired to the property. Example:
    Product.propTypes = 
    {
      // seller is now required.
      seller: PropTypes.any.isRequired,
    }​
  • If not, does the property have a default value?  

If yes, use defaultProps. Example:

Product.propTypes = 
{
  // price is optional.
  price: PropTypes.number,
}

// Add this new line before the export statement.
Product.defaultProps = 
{
  price: 0, // If price is not provided, price will be 0
}

export default Product;

6. Define property's value range

Does the property have predefined values?  

If yes, use oneOf(). Example:

Product.propTypes = 
{
  // size must have value of "small", "medium", or "large".
  size: PropTypes.oneOf(["small", "medium", "large"]),
}

7. Advanced typechecking

You can define a custom validator for your element. For example, here we want to check the length of name property:

Product.propTypes =
{
  name: function(props, propName, componentName) {
    if(props.name.length > 255) {
      return new Error(
        `'${propName}' props in ${componentName} needs to have max. length of 255`
      );
    }
    return null;
  }
}

 

That's it!