React's way of dealing with dataflow is one of it's greatest features in my mind. Each component renders based on information passed down by its parent, and might (or might not) have some internal state to supplement this data.
Some times though, you need to pass data from way up the component tree to somewhere deep down. Passing props to a component, only for that component to pass it down to some other component, is not a great pattern. This is where context comes in.
So what is context?
Context is a way to circumventing this wonderful dataflow, by passing data via a "back channel". It lets you pass data from one component (called a context provider) to another (called a context consumer) without passing data directly via props. They don't even have to be direct descendants of one another!
How does it look?
A context provider is a class component that implements the method getChildContext()
and the static property
childContextTypes
. Here's what it looks like:
class ThemeProvider extends React.Component {
static childContextTypes = {
color: PropTypes.string,
};
getChildContext() {
return { color: this.props.color };
}
render() {
return this.props.children;
}
}
This component takes a prop as color, and "provides" it as context to whoever wants to listen. Whenever the prop changes, the context will be updated.
A context consumer is one such component that wants to listen. It only requires the static property contextTypes
,
and will receive the context as this.context
(or as a second argument to a functional component). Here's an example
of how a consumer looks:
class ThemedButton extends React.Component {
static contextTypes = {
color: PropTypes.string,
};
render() {
return (
<button style={{ backgroundColor: this.context.color }}>
{this.props.children}
</button>
);
}
}
Note that both childContextTypes
and contextTypes
are using the prop-types
library - because context is
just data, and that library works great for describing data structures.
So why shouldn't I use it?
Although context is a supported feature that's not going away anytime soon, React warns you about using it. And reality is - it's with good reason.
First off, the context API is not considered stable, and is likely to change in future releases. Depending on it as of today would therefore provide your app with a potentially gruesome update path - and that's not cool.
Second, using context makes your components less reusable. You would have to wrap any context consumers in its matching context provider, or it just won't work.
Third, and most importantly, context won't be updated whenever an intermediate component returns false
from its
shouldComponentUpdate
. That means you might not get the updated context you were expecting.
So why do I care?
Turns out, a lot of libraries use context to pass information about stuff like state, internationalization and routing.
react-redux
does it, react-router
does it, and tons of others do the same. As it turns out, using libraries
who uses context below the surface is not such a bad thing. The library authors shield you from the complexities and
quirks of the context API and find ways around the update blocking problem.
Understanding how these libraries - which many developers look at like they're performing magic - is important. It's nothing more special than regular React, and creating your own almost-as-good version (or perhaps even better, given your use case) is far from impossible.
I hope you learned a tiny bit about context in today's article, and that you have a look at the attached resources below. I've found some great articles that go much further in explaining how this context thing works :)