{ Intermediate React Concepts. }

Objectives

By the end of this chapter, you should be able to:

  • Write more complex PropTypes
  • Understand additional tools that can be added for React codebases
  • Define what a Higher Order Component is
  • Explain how the React 16.3 Context API works and where context can be useful

PropTypes

One of the nice features of React is the ability to create PropTypes for props in our components. PropTypes are an excellent way to ensure that we are using the correct type (string / integer / boolean) as well as add additional validation for our props (make sure they exist). These validations will not throw errors if they are not met, but will return warnings letting a user know that they are not being met.

PropTypes are not only useful for yourself when building React applications, but since components are meant to be reused, they are essential for letting other developers know how exactly your components should be used. Let's start by importing the PropTypes function and creating a component with PropTypes.

To begin, create a new react project, and then import the prop-types module. This used to be built-in with React, but as of React version 15, it has been pulled out into a separate module.

npm i prop-types

Once you've installed prop-types, modify your App.js as follows:

import React, { Component } from "react";
import PropTypes from "prop-types";

export default class App extends Component {
  render() {
    return (
      <div>
        <h1>
          Hello {this.props.name}, you are {this.props.age} years old.
        </h1>
      </div>
    );
  }
}

App.propTypes = {
  name: PropTypes.string,
  age: PropTypes.number,
  data: PropTypes.object.isRequired
};

Then, in your index.js, pass in a name and an age as props:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";

ReactDOM.render(<App name="Alice" age={30} />, document.getElementById("root"));
registerServiceWorker();

If you start this app and head to the console, you'll see a warning because we didn't specify a data prop, even though we said it should be required!

There are many prop-types that you can add, all of which are members of the React.PropTypes object. Each prop type maps to a builtin JavaScript or React data structure. These include:

Prop Type What is it
React.PropTypes.array Array
React.PropTypes.bool Boolean
React.PropTypes.func Function
React.PropTypes.number Number
React.PropTypes.object Object
React.PropTypes.string String
React.PropTypes.symbol Symbol
React.PropTypes.element React element
React.PropTypes.node anything that can be rendered (element, number, string, etc.)

We can even create our own types of objects with certain keys and values and use PropTypes.shape({}) to customize our PropTypes.
We can also check if a prop is mapped to an instance of a class we have access to, for example React.PropTypes.instanceOf(MyClass).

To read about more intermediate and advanced uses of prop-types, check out this article from the React docs and this tutorial.

Type Checking with Flow

Remember how JavaScript is a weakly-typed and dynamically-typed? In other words, you never have to declare your variables as int or object as in statically-typed languages such as Java.

Well, doing type validation with prop-types is the first step in imposing a stronger typing system onto JavaScript.

As you can imagine, type validation has bug-prevention uses outside of just prop validation in React components. As such, many developers use type-checking tool such as Flow or TypeScript.
These tools are roughly equivalent and can both be used with React, but Flow is more popular among the React community as it is maintained by Facebook and integrates smoothly with React (in fact, it started as an internal tool for React devs), whereas TypeScript is by Microsoft.
Both tools have their own special syntax (which looks quite different than vanilla JS) which is removed when the final minified bundle is bulit.

A nice bonus is that if you use Flow, you never have to do your own prop type validation on components!

We won't be utilizing Flow in this session, but it is a widely-used and increasingly-popular tool that we recommend learning on your own.

Using Preact

Another tool that has been growing in popularity in the React community is Preact, which is a much smaller (3 KB minified) alternative to React with an almost identical API.

Preact strips out some of the additional modules that ship with React that you don't use frequently to minimize the file size of the library. Switching to Preact involves a little bit of work to set up and you can read more about it here.

Higher Order Components

One of the more powerful techniques you can use when building applications with React is have a component return a new component. The Facebook docs define a A higher-order component (HOC) as an advanced technique in React for reusing component logic.

HOCs are not part of the React API, they are a pattern that emerges from React's compositional nature.

Concretely, a higher-order component is a function that takes a component and returns a new component.

Higher order components are quite common in libraries we will see soon like React-Router and Redux. This behavior used to be accomplished with mixins which are an older and depricated feature of React.

To get started, follow along with the example here.

Immutability

A bit more of an advanced topic in React is the use of immutable data structures when building larger applications. We will revisit the topic of immutability when we talk about Redux.

You can read more about it here, here and here.

Context

The Context API is actually something that is not new to React 16.3 at all, but it has been completely re-done to be more public and accessible.

Where it is useful

As you build larger React applications you will find that you'll pass props down from parent to child components frequently. While this is not an issue, it becomes challenging when you have high level components that contain state that needs to be passed down to great-grandchild and even lower level components. This leads you to have to pass props through components that don’t necessarily care about so you can send it down to components that do care. This becomes an even bigger issue as you move components around. Here is an example:

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class App extends Component {
  state = {
    instructorName: "Michael"
  };
  render() {
    return (
      <div>
        <h1>Hello From App! Let's pass some data down!</h1>
        <Grandparent name={this.state.instructorName} />
      </div>
    );
  }
}

const Grandparent = props => (
  <div>
    <h1>Hello from Grandparent!</h1>
    <Parent parentName={this.props.name} />
  </div>
);

const Parent = props => (
  <div>
    <h1>Hello from Parent!</h1>
    <Parent childName={this.props.parentName} />
  </div>
);

const Child = props => (
  <div>
    <h1>Hello from Child!</h1>
    <h2>Here is the instructor {props.childName}!</h2>
  </div>
);

export default App;

Notice in this example above, we only really needed the prop in the Child component, yet we had to sent it through the Grandparent and Parent component. This is a trivial example, but it illustrates the issue with passing down data only through a parent-child relationship.

A common solution to this is to use a state management library like Redux as they allow you to get data from the store easily anywhere in the tree. In Redux (specifically when connecting React and Redux), all you have to do is use a component called <Provider /> and your state is accessible by any component.

Under the hood, these state management libraries along with the next library we're going to learn, react-router use the React context API! For almost all of it's lifetime, the Context API was something that the React docs actively discouraged developers from using since it was experimental and likely to change. However, as of React 16.3, the new Context API is now available!

Before we learn how to work with the API, it is very important to understand what the React docs say when they mention:

Don’t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to be accessed in many components at multiple levels.

Use context to share data that can be considered “global” for your components. This can include some kind of global theme, a currentUser, or any other piece of information that needs to be shared amongst multiple distant components.

Working with the Context API

The new context API consists of three parts:

  1. React.createContext which is passed the initial value. This returns an object with a Provider and a Consumer

  2. A Provider component, which accepts a prop called value. The prop must be called value and inside here we place data we want to provide other components with.

  3. A Consumer component, which can be used anywhere below the provider. It Requires a function as a child, which makes the syntax at first a bit challenging (this is also called a render prop).The function receives the current context value and returns a React node. The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the value argument will be equal to the defaultValue that was passed to createContext(). All Consumers are re-rendered whenever the Provider value changes.

A very simple example

import React, { Component, createContext } from "react";

const DataContext = createContext("elie");

class App extends Component {
  render() {
    return (
      <div>
        <h1>Hello From App! Let's pass some data down!</h1>
        <DataContext.Provider value="Elie">
          <Grandparent />
        </DataContext.Provider>
      </div>
    );
  }
}

const Grandparent = props => (
  <div>
    <h1>Hello from Grandparent!</h1>
    <Parent />
  </div>
);

const Parent = props => (
  <div>
    <h1>Hello from Parent!</h1>
    <Child />
  </div>
);

const Child = props => (
  <div>
    <h1>Hello from Child!</h1>
    <DataContext.Consumer>
      {instructorName => {
        return <h2>Here is the instructor {props.instructorName}!</h2>;
      }}
    </DataContext.Consumer>
  </div>
);

export default App;

Notice here we are simply passing down the prop only in the Child component and defining it in the App component

You can read quite a bit more about context here

Additional Resources

https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.52qvqdmg7

https://www.sitepoint.com/react-higher-order-components/

https://camjackson.net/post/9-things-every-reactjs-beginner-should-know

Exercise

Complete the Intermediate React Exercise

When you're ready, move on to React Design Patterns

Continue

Creative Commons License