March 25, 2015

React.js: Communication between Components with Contexts

by José Magalhães

React.js: Communication between Components with Contexts

For the past few months, we’ve been using React.js here at Jscrambler. We may even write about it again in the future, but this post is a short piece about a specific and very interesting React feature.

Since React v0.12 there’s a new yet undocumented feature that gives new possibilities on how to communicate between components, we’re talking about contexts. The feature is still undergoing changes, but it’s already being used in a lot of projects.

In brief, a context is an object that is implicitly passed from a component to its children, so by using contexts you don’t have to explicitly pass around a whole bunch of props to pass some contextual data. This was one of the not so elegant parts of React that went away when contexts were introduced.

Apart from the missing official documentation, as of the time of this writing, it’s very difficult to find accurate literature that sheds some light on how React contexts really work. We spent some time understanding its inner workings and using it here at JScrambler, so we thought to contribute with this blog post.

In this article, we’ll show you how to code a toggle that changes the context of the parent and a panel that changes its content given a different context.

Parent Component

var React = require('react');

var DummyWrapper = require('./dummy-wrapper');
var ItemToggle = require('./item-toggle');

var Parent = React.createClass({
    getInitialState: function() {
        return {}
    },
    childContextTypes: {
        activeItem: React.PropTypes.any
    },
    getChildContext: function() {
        return {
            activeItem: this.state.activeItem
        };
    },
    setActiveItem: function(item) {
        this.setState({
            activeItem: item
        });
    },
    render: function() {
        return ( < div >
            < ItemToggle setActiveItem = {
                this.setActiveItem
            }
            /> < DummyWrapper / >
            < /div>
        );
    }
});

module.exports = Parent;

childContextTypes is useful while in development because it validates the schema against the object returned from getChildContext. While in production this step is bypassed.

Here we render the ItemToggle, while passing down @setActiveItem as prop to allow the child to change the parent state (and context). We also render a DummyWrapper that will have a child that depends on the Parent component.

Protect your React App with Jscrambler

Item Toggle Component

var React = require('react');

var ItemToggle = React.createClass({
    onClick: function(type) {
        var item;

        switch (type) {
            case 'A':
                item = "Item A";
                break;
            case 'B':
                item = "Item B";
                break;
            default:
                throw new Error('Unimplemented type');
        }

        this.props.setActiveItem(item);
    },
    render: function() {
        return ( < div > Select an Item:
            < ul >
            < li onClick = {
                this.onClick.bind(this, 'A')
            } > Item A < li onClick = {
                this.onClick.bind(this, 'B')
            } > Item B < /ul> < /div>
        );
    }
});

module.exports = ItemToggle;

Here we receive a prop setActiveItem from the parent that allows us to set the React component that is going to be instanced and set in the parent context.

To keep this example simple, Item A and Item B are just strings. Of course, you could easily replace them with React components.

Dummy Wrapper Component

var React = require('react');

var ActiveItemPanel = require('./active-item-panel');

var DummyWrapper = React.createClass({
    render: function() {
        return <ActiveItemPanel / > ;
    }
});

This component is just to show how ActiveItemPanel will be able to access the context of the Parent component even though they’re not even directly related.

Active Item Panel Component

var React = require('react')

var ActiveItemPanel = React.createClass({
    contextTypes: {
        activeItem: React.PropTypes.any
    },
    render: function() {
        return ( < div >
            Active Item: {
                this.context.activeItem
            } < /div>
        );
    }
});

module.exports = ItemPanel;

Here we’re defining the context types that we’re receiving through contextTypes.
Besides this, we simply render @context.activeItem inside the component. As you can see we didn’t even pass any prop to this component. The context just lays there with no effort.

To play around with this example check this jsfiddle.

Conclusions

As of now, the API allows owner-ownee communication but it’s already announced that on React’s v0.14 release context will work on the parent-child relationship, which allows even more options than the former.

Though the API may change, this is extremely powerful and avoids a lot of explicit props to be passed from parents to children. At the same time, your component will be a little more coupled to where he’s being used, which in some scenarios may not be what you’re looking for.

Lastly, if you're building React applications with sensitive logic, be sure to protect them against code theft and reverse-engineering by following our guide.