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

109 lines
3.1 KiB
Markdown

# Step 2.4 - React Context (Demo)
[Lessons](../) | [Exercise](./exercise/) | [Demo](./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:
![](../../assets/todo-components.png)
## 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:
```ts
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`:
```js
<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:
```ts
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:
```js
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:
```js
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):
```js
const TodoFooter = props => {
const context = useContext(TodoContext);
return (
<div>
<button onClick={context.clear()}>Clear Completed</button>
</div>
);
};
```