Repository navigation
React Context API
This section expands on the previous by adding React Context API to
the Name That Pirate game. To prepare for the next exercise, do the following:
Navigate to the
context-apidirectory and run the following commands:npm install npm startOnce the app loads, notice that the behavior is identical to the previous version.
Open the
context-api\src\AppProvider.jsfile and study its contents. This is the only file that was added to the project.Examine the changes made to the
App.js,GameBoard.js, andScore.jsfiles.
React Context API allows data to be shared between components that do not have a parent/child relationship. It is the state management tool we currently use in "Marauder" and one which has grown in popularity since its release in late 2018. If you've ever used themes in the popular Styled Components tool for React, that library makes use of the Context API.
In the previous app, in order for the Score and GameBoard to share
data, the App component had to take responsibility for managing the
correct and incorrect counts. This code is shown below.
```javascript
// state-api App.js
...
constructor(props) {
...
this.state = {
correct: 0,
incorrect: 0,
};
...
}
handleCorrectAnswer() {
this.setState({
correct: this.state.correct + 1,
});
}
handleIncorrectAnswer() {
this.setState({
incorrect: this.state.incorrect + 1,
});
}
render() {
...
<GameBoard
onCorrectAnswer={this.handleCorrectAnswer}
onIncorrectAnswer={this.handleIncorrectAnswer}
/>
<Score correct={this.state.correct} incorrect={this.state.incorrect} />
...
}
...
```
Instead of relying on the highest-level component to keep track of things, React Context API creates a context that can be wrapped around the components involved. It's an approach best used in applications where you have several components that use and/or modify the same data but are separately and deeply nested within the component tree.
Here is the code for our context provider in our Pirate game:
``` javascript
// App.js
export const AppContext = React.createContext();
export default class AppProvider extends React.Component {
state = {
correct: 0,
incorrect: 0,
incCorrect: () => {
this.setState({correct: this.state.correct + 1});
},
incIncorrect: () => {
this.setState({incorrect: this.state.incorrect + 1});
},
};
render() {
return (
<AppContext.Provider value={this.state}>
{this.props.children}
</AppContext.Provider>
);
}
}
```
The AppProvider defined above gets wrapped around GameBoard and
Score so that they can share state.
``` javascript
// App.js
...
<AppProvider>
<GameBoard />
<Score />
</AppProvider>
...
```
The Score component uses AppContext.Consumer to display state
contained in the context.
``` javascript
// Score.js
...
<AppContext.Consumer>
{context => context.correct}
</AppContext.Consumer>
...
```
The GameBoard component uses the functions defined in the context to
update state stored in the context.
``` javascript
// GameBoard.js
...
handleAnswerSelected(pirate, context) {
...
context.incCorrect();
...
context.incIncorrect();
...
}
...
<AppContext.Consumer>
{context => {
return this.state.turn.possiblities.map(p => (
<Answer
key={p.name}
pirate={p}
disabled={p.disabled}
onSelected={p => this.handleAnswerSelected(p, context)}
/>
));
}}
...
```
- Add a button to the Score component in the location shown below.
<AppContext.Consumer> {context => context.incorrect} </AppContext.Consumer> </span> </span> </h3> // HERE THERE BE A BUTTON </div>
- Add a click handler to the new button that resets the correct and incorrect counts to 0
- Simple
- Little to no hidden logic, i.e., no "magic."
- Data sharing is possible without a Parent/Child relationship
- Built into React negating the need for external libraries thus preventing conflicts and update woes.
- The context can become unwieldy as it grows.