Files
frontend-bootcamp/step2-04/demo
2019-03-02 20:57:25 -08:00
..
2019-03-02 20:57:25 -08:00
2019-03-02 20:57:25 -08:00

Step 2.4 - React Context (Demo)

Lessons | Exercise | Demo

In this step, we describe some problems we encounter when creating a more complex application.

We will solve these problems with the React Context API. The Context API consists of:

  1. Provider component
  2. Consuming context from a Class Component
  3. Consuming context from a Functional Component

For a single component, React gives us a mental model like this:

(props) => view;

In a real application, these functions are composed. It looks more like this:

Problems in a Complex Application

  1. Data needs to be passed down from component to component via props. Even when some components do not need to know about some data.

  2. There is a lack of coordination of changes that can happen to the data

Even in our simple application, we saw this problem. For example, <TodoList> has this props interface:

interface TodoListProps {
  complete: (id: string) => void;
  remove: (id: string) => void;
  todos: Store['todos'];
  filter: FilterTypes;
  edit: (id: string, label: string) => void;
}

All of these props are not used, except to be passed down to a child Component, TodoListItem:

<TodoListItem todos="{todos}" complete="{complete}" remove="{remove}" edit="{edit}" />

Context API

Let's solve the first one with the Context API. A context is a special way for React to share data from components to their descendant children components without having to explicitly pass down through props at every level of the tree.

We create a context by calling createContext() with some initial data:

const TodoContext = React.createContext();

Now that we have a TodoContext stuffed with some initial state, we will wrap TodoApp component with TodoContext.Provider so that it can provide data to all its children:

class TodoApp extends React.Component {
  render() {
    return (
      <TodoContext.Provider
        value={{
          ...this.state,
          addTodo={this._addTodo},
          setFilter={this._setFilter},
          /* same goes for remove, complete, and clear */
        }}>
        <div>
          <TodoHeader />
          <TodoList />
          <TodoFooter />
        </div>
      </TodoContext.Provider>
    );
  }
}

Inside the children components, like the <TodoHeader> component, the value can be access from the component's context prop like this:

class TodoHeader extends React.Component {
  render() {
    // Step 1: use the context prop
    return <div>Filter is {this.context.filter}</div>;
  }
}

// Step 2: be sure to set the contextType property of the component class
TodoHeader.contextType = TodoContext;

If you're using the functional component syntax, you can access the context with the useContext() function (we are using the function passed down inside the context, in this case):

const TodoFooter = props => {
  const context = useContext(TodoContext);
  return (
    <div>
      <button onClick={context.clear()}>Clear Completed</button>
    </div>
  );
};