Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.
React components can only pass data from parent to child via props. The Context API adds to that by allowing components with other relationships to share data.
In this article, we’ll look at how to use it to share data between components.
When to Use Context
We should use Context to share data between React components. However, it should be used sparingly since it creates tight coupling between components.
To use it within a simple app, we can write the following:
const ColorContext = React.createContext("green");
class Button extends React.Component {
render() {
return (
<div>
<ColorContext.Consumer>
{value => (
<button style={{ color: value }}>{this.props.children}</button>
)}
</ColorContext.Consumer>
</div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<ColorContext.Provider value="blue">
<Button>Click Me</Button>
</ColorContext.Provider>
</div>
);
}
}
In the code above, we created a Context to share data by writing:
const ColorContext = React.createContext("green");
createContext
takes a default value as an argument, where we passed in 'green'
.
Then in the App
component, we have the ColorContext.Provider
component with the value
prop set to the value that we want to share.
In the example above, it’ll be 'blue'
. We wrapped it around the components that we want to share the data with so that we can access the value from that component.
In this case, we created a new Button
component, which has the ColorContext.Consumer
component. Inside it, we can get the value shared from the context provider from the value
parameter in the function we inserted inside the ColorContext.Consumer
component.
value
should be set to 'blue'
since that’s what we set as the value of the value
prop.
Inside the function we passed in the consumer, we returned a buttom
element with the style prop and we set the color
style to value
, which is 'blue'
.
Alternatives to Context
If we want to pass data into a deeply nested component, we can instead pass in the whole component down to where we want it. This way, we don’t have to worry about passing props to multiple levels to pass something that’s only needed by deeply nested components.
For example, if we want to pass the color prop to Button
components, which is contained in a ButtonBar
. We can do that as follows:
class Button extends React.Component {
render() {
return (
<button style={{ color: this.props.color }}>{this.props.children}</button>
);
}
}
class ButtonBar extends React.Component {
render() {
return this.props.buttons;
}
}
class App extends React.Component {
render() {
const buttons = [
<Button color="blue">Click Me</Button>,
<Button color="green">Click Me 2</Button>
];
return <ButtonBar buttons={buttons} />;
}
}
In the App
component, we have the Button
components in the buttons
array. Then we passed the whole array straight down to the ButtonBar
component.
Then ButtonBar
just returns what we passed in, which is this.props.buttons
.
This also means more complexity in the higher-order components, so it may not be suitable in all cases.
Updating Context from a Nested Component
We can pass in functions to the object that we pass into createContext
so that we can call them inside the component that has the context consumer component.
For example, we can write the following:
const colorObj = {
color: "green",
toggleColor: () => {}
};
const ColorContext = React.createContext(colorObj);
class Button extends React.Component {
render() {
return (
<div>
<ColorContext.Consumer>
{({ color, toggleColor }) => (
<button onClick={toggleColor} style={{ color }}>
{this.props.children}
</button>
)}
</ColorContext.Consumer>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
color: "blue",
toggleColor: () => {
this.setState(state => ({
color: state.color === "green" ? "blue" : "green"
}));
}
};
}
render() {
return (
<div>
<ColorContext.Provider value={this.state}>
<Button>Click Me</Button>
</ColorContext.Provider>
</div>
);
}
}
The code above starts with defining the colorObj
object, which is passed into createContext
as the default value of ColorContext
.
Then in the App
component, we initialize this.state
by setting it to an object with the toggleColor
function, and the color
property set to 'blue'
.
We pass this.state
as the value of the value
prop of ColorContext.Provider
.
Then we access the whole object inside the ColorContext.Consumer
component in the Button
component.
Inside there, we get the color
and toggleColor
property from the this.state
which we passed in from the ColorContext.Provider
. Then we pass toggleColor
into the onClick
prop, and color
into the object that we passed into the style
prop.
Then when we click the Click Me button, the text color will toggle between blue and green.
Consuming Multiple Contexts
We can consume multiple contexts by nesting them. For example, we can do that as follows:
const ColorContext = React.createContext("green");
const BorderContext = React.createContext("");
class Button extends React.Component {
render() {
return (
<div>
<ColorContext.Consumer>
{color => (
<BorderContext.Consumer>
{border => (
<button style={{ color, border }}>{this.props.children}</button>
)}
</BorderContext.Consumer>
)}
</ColorContext.Consumer>
</div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<ColorContext.Provider value="blue">
<BorderContext.Provider value="3px solid green">
<Button>Click Me</Button>
</BorderContext.Provider>
</ColorContext.Provider>
</div>
);
}
}
In the code above, we create 2 Contexts, ColorContext
and BorderContext
and passed in values to the value
prop to both. We nested the providers in the App
component, which means that both contexts can be consumed by the Button
component inside.
Then in the Button
component, we have consumers for both contexts nested in each other. And then we can get both values that were passed in from the providers.
We then use both values to set the styles of the button
.
In the end, we a button with blue text and a thick green border.
Conclusion
We can use the React Context API to share data between components.
It works by creating a Context object with React.createContext
. Then we wrap the context provider component outside the components that we want to consume the context from.
Then in the component that we put inside the provider, we have the context consumer component wrapped outside of whatever we want to apply the context value to.
Finally, we can get the value inside the function that we pass inside the Context consumer.