Props and PropTypes in React

Avatar of Kingsley Silas
Kingsley Silas on

UGURUS offers elite coaching and mentorship for agency owners looking to grow. Start with the free Agency Accelerator today.

React encourages developers to build by breaking a UI up into components. This means there will always be a need to pass data from one component to another — more specifically, from parent to child component — since we’re stitching them together and they rely on one another.

React calls the data passed between components props and we’re going to look into those in great detail. And, since we’re talking about props, any post on the topic would be incomplete without looking at PropTypes because they ensure that components are passing the right data needed for the job.

With that, let’s unpack these essential but loaded terms together.

Props: The data being passed around

Basically, props are what make React the tool that it is. React was designed to break things down into pieces that are served when they are needed. Props are defining characteristics stored by those pieces and they are accessed and sent when they’re requested. The result is a screen that renders only what it needs and nothing more, speeding up page loads and boosting overall performance.

These data can come in different forms: namely, strings, array and functions. The ability to pass data between components, so let’s break down specifically how to access and pass data.

Passing and accessing props

Let’s say we are working on an application that shows a list of interesting demos pulled from CodePen:

See the Pen Props Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

We can illustrate the app as a collection of components:

The list of pens is going to require data, notably the title, URL and author for each demo that the app displays. We can construct that data like so:

const pensList = [
  {
    title: "Elastic Input[Google Chrome]",
    url: "https://codepen.io/andreasstorm/pen/JBGWBa",
    author: "Andreas Storm"
  },
  {
    title: "Phenomenon instances!",
    url: "https://codepen.io/cvaneenige/pen/ajNjaN",
    author: "Colin van Eenige"
  },
  {
    title: "cpc-forms experiment with css variables",
    url: "https://codepen.io/terabaud/pen/YjwYKv",
    author: "Lea Rosema"
  },
  {
    title: "Nuotron Logo Animation with Hover Effect",
    url: "https://codepen.io/YahiaRefaiea/pen/YjyZLm",
    author: "Yahia Refaiea"
  }
];

The App component will pull the data. Here’s the basic structure for that component:

const App = () => {
  return (
    <div>
      <PenList pens={pensList} />
    </div>
  );
}

We are passing an array of pens as a prop to the PenList (which we’ll create in just a bit). The parent component (PenList) accesses the data (penList), which gets passed as pens props to the child component (Pen).

const PenList = props => {
  return (
    <React.Fragment>
      <h2>Interesting Pens on CodePen</h2>
      <ul>
        {props.pens.map(pen => {
          return (
            <li key={pen.url}>
              <Pen {...pen} />
            </li>
          );
        })}
      </ul>
    </React.Fragment>
  );
};

The PenList component loops through the pens props (props.pens) to return each item as a Pen component. If this was a class component, we would prefix the pens props with this, like this:

class PenList extends React.Component {
  render() {
    return (
      <React.Fragment>
        <h2>Interesting Pens on CodePen</h2>
        <ul>
          {
            this.props.pens.map(pen => {
              return (
                <li key={pen.url}>
                  <Pen {...pen} />
                </li>
              )
            })
          }
        </ul>
      </React.Fragment>
    )
  }
}

The next thing to note is the use of key in the example. A key is a unique identifier we can assign to each item in our list to make sure we can distinguish between items. In this case, we’re mapping the key to the URL of each pen. There’s no chance of two items being the same, so it’s a good piece of data to use for this purpose.

The remaining properties are passed as props to the Pen component. Here’s the Pen component making use of those props:

const Pen = props => {
  return (
    <div>
      <p>
        [{props.title}]
      </p>
      <p>Made by: {props.author}</p>
    </div>
  );
};

Note that we are constructing the Pen component (const Pen) rather than defining it as a class (class PenList) like we did for the PenList component. As such, we can access the values using props. That’s a handy little shorthand we can use instead of re-mapping Pen to the data. The parent already has it, so let’s just pass it along!

Passing functions using props

We just looked at passing an array of data as props from one component to another, but what if we’re working with functions instead? React allows us to pass functions between components, but it’s quite technical. Still, it’s something you’d want to do for specific use cases and worth us looking into.

Let’s use a simple example, say an app that allows you to create a list of tasks. You know, a to-do list, like for chores, projects or what have you. In this app, the list of tasks is contained in the App component, which is the parent component. The Todo component will be the child in this scenario, and its sole job will be to list each task that gets created.

In true to-do list form, we don’t just want to create tasks, but be able to remove them once a task has been created. Since the to-do list is contained in the App component, we have to be able to identify the specific item the user wants to remove from the list by obtaining the id and then remove the item in the App component.

Sound complex? Here’s what we’re going for:

See the Pen Props Pen 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Broken down into code:

let todoCounter = 1;

class App extends React.Component {
  state = {
    list: [],
    item: ""
  };

  handleInputChange = event => {
    this.setState({ item: event.target.value });
  };

  handleSubmit = event => {
    event.preventDefault();
    const item = {
      id: todoCounter++,
      value: this.state.item.slice()
    };
    this.setState({
      list: this.state.list.concat(item),
      item: ""
    });
  };

  handleRemove = id => {
    this.setState({
      list: this.state.list.filter(c => c.id !== id)
    });
  };

  render() {
    return (
      <React.Fragment>
        <h2>Add Todo</h2>
        <div>
          <input
            type="text"
            value={this.state.item}
            onChange={this.handleInputChange}
          />
        </div>
        <div>
          <button type="submit" onClick={this.handleSubmit}>
            Add
          </button>
        </div>
        <div>
          <h3>Lists</h3>
          <ul>
            {this.state.list.map(item => {
              return (
                <li key={item.id}>
                  <Todo {...item} removeTodo={this.handleRemove} />
                </li>
              );
            })}
          </ul>
        </div>
      </React.Fragment>
    );
  }
}

Notice that we defined todoCounter at the top and set it to 1. We created this so we can have unique keys for the to-do items just like we did when we used URLs for the list of pens in our previous example.

The method for deleting tasks is created in the App component. In the render() function, we pass the to-do properties as props to the Todo component. We also pass the handleRemove() function as a prop named removeTodo(). We will use this in the Todo component which looks like this.

class Todo extends React.Component {
  deleteTodo = id => {
    this.props.removeTodo(id);
  };
  render() {
    return (
      <div>
        {this.props.value}
        <button onClick={() => this.deleteTodo(this.props.id)}>X</button>
      </div>
    );
  }
}

We have to pass the id of the to-do item to removeTodo() in the Todo component because we cannot update the state of the App component without it. This is essentially how we are able to pass a function between components using props — pretty similar to how we did it with an array, the difference being we’re passing functionality around instead of raw data.

PropTypes

PropTypes ensure that the right type of props is passed to a component — and, conversely, that the receiving component is receiving the right type of props.

We can think about them like a football quarterback passing the ball to a receiver. The quarterback only wants his players to receive the ball. And, for that matter, the quarterback wants the receiver to catch a ball — not a cat, pickle or taxi. PropTypes would ensure that the correct object (a ball) is being passed and that it is passed to the correct receiver (player on the team).

(If only football had PropTypes in real life!)

To make use of PropTypes, you have to add the package as a dependency to your application by running yarn add prop-types in the command line.

We can use PropTypes in our app that displays interesting pens. Here is how we will use it for the Pen component:

Pen.propTypes = {
  title: PropTypes.string,
  url: PropTypes.string,
  author: PropTypes.string
};

We’re declaring that the props for title, url and author should be strings. Not numbers. Not functions. Strings and strings alone.

If we happened to the props of author to a number instead of a string like this:

author: PropTypes.number

…we will get an error:

Warning: Failed prop type: Invalid prop `author` of type `string` supplied to `Pen`, expected `number`.

So, PropTypes are useful in catching bugs. We can also enforce passing props by using isRequired:

Pen.propTypes = {
  title: PropTypes.string.isRequired,
  url: PropTypes.string.isRequired,
  author: PropTypes.string.isRequired
};

The basic data types you will need include string, number, boolean, function, etc.

Person.propTypes = {
  email: PropTypes.string,
  age: PropTypes.number,
  availability: PropTypes.bool,
  handleSubmit: PropTypes.func
}

There are more types available and tons of documentation on them.

In cases where a prop is optional (i.e. not using isRequired), you can set a default value to make sure something gets passed:

Developer.defaultProps = {
  language: 'JavaScript' 
}

With this, the language prop will always have a value when it used — even if one isn’t provided.

Wrap Up

Well, that’s a broad look at props in React. It’s pretty much a guarantee that you will use both props and propTypes in a React application. Hopefully this post shows just how important they are to React as a whole because, without them, we have nothing to pass between components when interactions happen. They’re very much a core part of the component-driven and state management architecture that React is designed around.

And propTypes are an added bonus — like a built-in quality assurance checker for catching bugs and letting us know about them. Nice to know that they’ve got our back as we work.