React's context feature in practice
Once we started using React in our projects, our productivity went up. We were able to deliver complex view with many moving parts in a fraction of a time we would need before with other technologies.
Our application grew with time and some of our applications got really complex views. In some point, it was really inconvenient to pass down some common data in large component trees. It wasn’t a no-go, but a bit tiring.
The problem
Let’s see this view with crazy-nested structure in one of our projects. There were a lot of actions that could be triggered from that view – you could inline-edit the names of all elements, click a button to pop-up a modal with another application.
All of the actions on the view above were triggered using the eventing system. So if I wanted to edit the name of a threat, I had to fire an event to inform appropriate app about that:
this.props.eventBus.publish('threatNameEdited', newName);
We were passing the eventBus
with the props. You could imagine how
inconvenient it was to pass this object through at least 4-levels deep
components tree.
Contexts for the rescue
Starting from version 0.12, React.js developers added the contexts feature into a codebase. It basically lets you to pass some data through all nodes in components tree.
You need to remember that this feature is still in development and its API may change in next version of React.
Context is something that works similarly as properties and state. But, instead of explaining it in long sentences, let’s see the example.
Quick Example
We defined 2 components: parent and its child.
React.createClass({
displayName: 'Parent',
render: function() {
return <Child />;
}
});
React.createClass({
displayName: 'Child',
render: function() {
return <div>Yo!</div>;
}
});
Let’s say, we want to propagate eventBus
down the components tree, with root
in Parent
.
The root of the context must define how the context looks like and what type
each element in it has. In our case, Parent
is a root.
It’s also worth noting that the component which created the context, cannot access it directly.
React.createClass({
displayName: 'Parent',
// Define how the context looks like
getChildContext: function() {
return {
eventBus: this.props.eventBus
}
}
// Define types of elements in context
// We define it the same way as `propTypes`
childContextTypes: {
eventBus: React.PropTypes.object.isRequired
},
render: function() {
return <Child />;
}
});
The components below the root don’t get the access to a context automatically. They must specify what items from contexts they are interested in.
React.createClass({
displayName: 'Child',
// That's the only thing you need to add
contextTypes: {
eventBus: React.PropTypes.object.isRequried
},
render: function() {
return <div>Yo!</div>;
}
});
And from now on, you can access the eventBus
from a Child
component using
this.context.eventBus
.
What’s more, you can access context from lifecycle methods like
componentWillUpdate
, shouldComponentUpdate
and so
on.
Context is usually passed as the last argument.
When not to use contexts?
Contexts aren’t the best way pass domain related models. They couple your code and make it less reusable. The readability of the code may also hurt.
Have in mind what I mentioned before - contexts feature is still under development. React developers don’t guarantee that the behaviour and API will stay the same. You should try to isolate parts of your systems using contexts.
When to use contexts?
If you look at the data that is shared between most of your components, you may find some elements that are commonly used. Here are some examples from projects I worked on:
- Current user model
- Eventing bus
- Store in Flux library
- Session storage (theme or language settings)
More
For more information, I recommend checking out the official React docs. They are the great written and reliable source of information.
If you’re interested in checking out event-bus I mentioned in the article, check out our NPM package and its GitHub repository. It a lightweight package with powerful possibilities. And it has absolutely no dependencies!