Carl Rippon

Building SPAs

Carl Rippon
BlogBooksAbout
This site uses cookies. Click here to find out more

Playing with the Context API in React 16.3

May 08, 2018
react

With React 16.3 recently released I thought it was time to take a look at the new context API. The new context API does what it says on the tin - it allows components to get data from a context rather than from props. It prevents having to pass props down through child component through child component … i.e. “prop drilling”.

Prop drilling v context API

So, let’s give this try for allowing a couple of components to consume data about a tenant in a multi-tenant app. The first child component will show the name of the tenant. The second child component will show a form capturing a name and age. Whether the age is captured depends on a promptForAge flag in the tenant.

Prop drilling v context API

Container component

The shell of our container component is below. It gets the tenant data from a web service and puts it in its state. We are referencing child components Title and ContactForm but we aren’t using the new context API yet.

class App extends Component {
  state = {
    tenant: null
  }

  componentDidMount() {
    fetch("http://localhost:21246/api/tenant")
      .then((response) => {
        return response.json();
      })
      .then(data => {
        this.setState({ tenant: data });
      }).catch(error => {
        console.log(error);
      });
  }
  render() {
    const {tenant} = this.state;
    return (
      {tenant &&
        <div className="container">
          <Title />
          <ContactForm />
        </div>
      }
    );
  }
}

Let’s create our context. We’ll call it TenantContext.

const TenantContext = React.createContext();

Now let’s specify the context provider around the child components in our container component passing in the tenant as the value:

render() {
  const {tenant} = this.state;
  return (
    <TenantContext.Provider value={tenant}>      {tenant &&
        <div className="container">
          <Title />
          <ContactForm />
        </div>
      }
    </TenantContext.Provider>  );
}

Title component

The context is now setup nicely in our container component. This is our Title component without consuming TenantContext:

function Title() {
  return <h1>{/* TODO: inject the tenants name  */}</h1>;
}

So, let’s consume TenantContext using the render props pattern:

function Title() {
  return (
    <TenantContext.Consumer>      {tenant => <h1>{tenant.name}</h1>}    </TenantContext.Consumer>  );}

ContactForm component

Moving on to our ContactForm component, we again consume TenantContext using the render props pattern. We use tenant.promptForAge to conditionally render the age input. In our simple example, we only submit the details to the console.

class ContactForm extends React.Component {
  state = {
    name: "",
    age: 0
  };

  handleSubmit(e) {
    console.log(this.state);
    e.preventDefault();
  }

  render() {
    const { name, age } = this.state;
    return (
      <TenantContext.Consumer>
        {tenant => (
          <form onSubmit={e => this.handleSubmit(e)}>
            <p>Enter your details ...</p>
            <div className="form-group">
              <label htmlFor="name">Name</label>
              <input
                type="text"
                className="form-control"
                id="name"
                value={name}
                onChange={e => this.setState({ name: e.target.value })}
              />
            </div>
            {tenant.promptForAge && (
              <div className="form-group">
                <label htmlFor="age">Age</label>
                <input
                  type="number"
                  className="form-control"
                  id="age"
                  value={age}
                  onChange={e => this.setState({ age: e.target.value })}
                />
              </div>
            )}
            <button type="submit" className="btn btn-primary">
              Save
            </button>
          </form>
        )}
      </TenantContext.Consumer>
    );
  }
}

Wrap up

The new react Context API is super simple to use and is a great alternative to “prop drilling” for sharing state across multiple components.


Want to learn more about React and TypeScript? Check out my book
Learn React with TypeScript 3