If we take a look at React component we can outline some properties.

State

Yes, each react component has state. State is something internal to the component. Only the component it self can read and write into its own state and as the name implies, the state of the component is used to store state (captain obvious here). Not interesting. Let’s move next.

Props

Or shall we say properties. Props is the data that affects the component display and behavior. Props can be optional or mandatory, and they are provided by the parent component. Ideally, when you provide your component with the same props — it renders the same. Not interesting as well. Let’s move on.

Context

Meet context, the reason I’m writing this post. Context is an undocumented feature of react.js and it is similar in a way to props, but the only difference is that props are passed explicitly by parent to child, and they do not propagate down the hierarchy, while context can be requested by children component implicitly.

But why?

Good question. Lets draw!

We have a Grandparent component that renders Parent A component that renders two child components: Child A and Child B.

Lets say Grandparent component knows something that Child A and Child B wants to know as well, but Parent A doesn’t care about. Lets call this piece of data Xdata. How would Grandparent component give ChildA and ChildB access to Xdata?

Well, using Flux architecture we can store Xdata inside a store and let Grandparent, Child A and Child B subscribe to that store. But what if we want Child A and Child B to be pure dumb components that only renders some markup?

Well, we than can pass Xdata as prop to Child A and Child B. But Grandparent can not pass props to its grand children without going through Parent. And its not that big deal if we have only 3 level nesting, but real applications have a lot more nesting levels where top components acts as containers and leaf components are just markup. Well, we can use mixins that will automatically pass the props down the hierarchy for us, but its not elegant.

Or we can use context. As I said earlier, context allows children component to request some data to arrive from component that is located higher in the hierarchy.

Here is how it looks:



var Grandparent = React.createClass({

childContextTypes: {

name: React.PropTypes.string.isRequired

}, getChildContext: function() {

return {name: 'Jim'};

},



render: function() {

return <Parent/>;

}



}); var Parent = React.createClass({

render: function() {

return <Child/>;

}

}); var Child = React.createClass({

contextTypes: {

name: React.PropTypes.string.isRequired

}, render: function() {

return <div>My name is {this.context.name}</div>;

}

}); React.render(<Grandparent/>, document.body);

And here is a JSBin with the code. Change Jim to Jack and you see how your component re-renders.

WHAT IS HAPPENING?!

Our Grandparent component says two things:

I provide my children with context type named name of type string. This is what happening in childContextTypes declaration. The value of context type named name is Jim. This is what happening in getChildContext function.

And our child component just says “Hey I expect to have context type named name!” and it gets it. As far as I understand (and I’m far from being expert in react.js internals) when react renders children components it checks which components want to have context and those that want context are provided with context if parent supplies them.

Cool!

Yep, expect when you encounter the following error

Warning: Failed Context Types: Required context `name` was not specified in `Child`. Check the render method of `Parent`.

runner-3.34.3.min.js:1 Warning: owner-based and parent-based contexts differ (values: `undefined` vs `Jim`) for key (name) while mounting Child (see: http://fb.me/react-context-by-parent

Yes of course I checked the link, its not very helpful.

Here is the code that causes this (JSBin):

var App = React.createClass({

render: function() {

return (

<Grandparent>

<Parent>

<Child/>

</Parent>

</Grandparent>

);

}

}); var Grandparent = React.createClass({

childContextTypes: {

name: React.PropTypes.string.isRequired

}, getChildContext: function() {

return {name: 'Jim'};

},



render: function() {

return this.props.children;

}



}); var Parent = React.createClass({

render: function() {

return this.props.children;

}

}); var Child = React.createClass({

contextTypes: {

name: React.PropTypes.string.isRequired

}, render: function() {

return <div>My name is {this.context.name}</div>;

}

}); React.render(<App/>, document.body);

It doesn’t make sense now, but Ill explain a setup where this is a viable hierarchy in the end of this post.

So what is happening?

It took me a lot of time to understand why this happens. Googling the error only brought me to discussions by people who also wonders why this happens. I looked at other projects like react-router or react-redux that uses context to pass data down the component tree, when eventually I realized what is wrong.

Note: I said I’m not expert in react internals, so everything I’m going to say now is purely what I understood about react internals. Leave a comment if you think I’m wrong (preferably with a link that backups your comment).

Remember I said that each react component have state, props and context? Well, each component have as well so called parent and owner. And if we follow the link from the earlier warning (so yes, it is useful, I lied) we can understand that:

In short, the owner of a component is whomever creates the component, while the parent of a component is whomever would be the containing ancestor in the DOM hierarchy.

It took me some time to understand this statement.

So in my first example, the owner of Child component is Parent, as well as the parent of Child is Parent. While in the second example the owner of Child component is App while the parent is Parent.

Context is something that propagates in a strange way to all the descendant components, but will be assigned only to those components who explicitly asked for context. But context does not propagate from the parent component as you’d expect but from owner! And since the owner of Child is App, React tries to find the “name” attribute in context of App instead of Parent or Grandparent.

Here is the relevant bug report in React. And here is the relevant pull request that should fix this to Parent based context as opposed to Owner based context in React 0.14.

However React 0.14 is not there yet, so here is a fix (JSBin):

var App = React.createClass({

render: function() {

return (

<Grandparent>

{ function() {

return (<Parent>

<Child/>

</Parent>)

}}

</Grandparent>

);

}

}); var Grandparent = React.createClass({

childContextTypes: {

name: React.PropTypes.string.isRequired

}, getChildContext: function() {

return {name: 'Jack'};

},



render: function() {

var children = this.props.children;

children = children();

return children;

}



}); var Parent = React.createClass({

render: function() {

return this.props.children;

}

}); var Child = React.createClass({

contextTypes: {

name: React.PropTypes.string.isRequired

}, render: function() {

return <div>My name is {this.context.name}</div>;

}

}); React.render(<App/>, document.body);

Instead of instancing the Parent and Child components inside App, we return a function. Then inside the Grandparent we call this function hence making Grandparent the owner of Parent and Child components. Context propagates as expected now.