I offer the readers of Habrakhabr a translation of the article “The land of undocumented react.js: The Context” .If we look at the React component, we can see some properties.
State
Yes, each React component has a state. This is something inside the component. Only the component itself can read and write to its own state and, as the name implies, the state is used to store the state of the component (Hi, Cap). Not interesting, let's go further.
')
Props
Or, say, properties. Props are data that affects the display and behavior of a component. Props can be optional or optional and are provided through the parent component. Ideally, if you transfer the same Props to your component, it will render the same. Not interesting, let's move on.
Context
Meet the context, the reason why I wrote this post. Context is an undocumented feature of React and is similar to props, but the difference is that props is passed exclusively from the parent component to the child and they do not propagate down the hierarchy, while the context can simply be requested in the child.
But how?
Good question, let's draw!

We have a Grandparent component that renders the Parent A component, which renders the Child A and Child B components. Let the Grandparent component know something that Child A and Child B would like to know, but Parent A does not need it. Let's call this piece of Xdata data. How would Grandparent pass Xdata to Child A and Child B?
Well, using the
Flux architecture, we could store Xdata inside the store and allow Grandparent, Child A and Child B to subscribe to this store. But what if we want Child A and Child B to be pure
stupid components that just render some markup?
Well, then we can pass Xdata as props to Child A and Child B. But Grandparent cannot push props to Child A and Child B without transferring them to Parent A. And this is not such a big problem if we have 3 levels of nesting, but in a real application, there are many more levels of nesting, where the top components act as containers, and the bottommost ones act like regular markup. Well, we can use mixins to let props automatically go down the hierarchy, but this is not an elegant solution.
Or we can use context. As I said earlier, context allows child components to request some data so that they come from a component located higher in the hierarchy.
What it looks like:
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
JSBin with code. Change Jim to Jack and see how your component will be re-rendered.
What happened?
Our grandparent component says two things:
1. I provide my descendants of string with the property (context type) name. This is what happens in the childContextTypes declaration.
2. Property value (context type) name - Jim. This is what happens in the getChildContext method.
And our child components simply say “Hey, I expect the context type name!” And they get it. As far as I understand (I am far from being an expert in the guts of React.js), when react renders child components, it checks which components they want to have in the context and those that they want to receive, if the parent component allows it (delivers the context).
Cool!
Yes, wait for 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, it is not very useful.
This code is the reason for 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 has no meaning now. At the end of the post I will explain how to set up a viable hierarchy.
It took me a long time to understand what was going on. Attempts to google the problem gave out only discussions of people who also encountered this problem. I looked at other projects like react-router or react-redux, which use context to push data down the component tree, when I finally realized what the error was.
Remember, I said that each component has a state, props and context? Also, each component has a so-called parent (parent) and owner (owner). And if we follow the link from warning (yes, it’s useful, I lied) we can understand that:
In short, the owner is the one who created the component when the parent is the component that is higher in the DOM tree.
It took me a while to understand this statement.
And so, in my first example, the owner of the Child component is Parent, the parent of the Child component is also Parent. While in the second example, the owner of the Child component is the App, when the parent is the Parent.
Context is something that strangely applies to all descendants, but will only be available from those components who explicitly asked for it.
But context does not extend from the parent, it extends from the owner. And still the owner of the Child component is an App, React is trying to find the name property in the context of an App instead of Parent or Grandparent.
Here is the corresponding
bug report in React. And a
pull request that should fix a context based on the parent in React 0.14.
However, React 0.14 is not there yet.
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 instances of the Parent and Child components inside the App, we return a function. Then inside Grandparent we will call this function, therefore we will make Grandparent the owner of the Parent and Child components. Context extends as it should.
OK, but why?
Remember my previous
article about localization in react? Considered the following hierarchy:
<Application locale="en"> <Dashboard> <SalesWidget> <LocalizedMoney currency="USD">3133.7</LocalizedMoney> </SalesWidget> </Dashboard> </Application>
This is a static hierarchy, but usually you have routing and ultimately you will create a situation where the owner and parent of your lower component will differ.
Application is responsible for loading locale and initializing the jquery / globalize instance, but it does not use them. You do not localize your top-level component. Typically, localization affects the lowest components, such as text nodes, numbers, money, or time. And I talked earlier about three possible ways of dragging a globalize instance down the component tree.
We store globalize in the store and allow the lowest components to subscribe to this store, but I think this is incorrect. Bottom components must be clean and stupid.
Dragging an instance of globalize as props can be tedious. Imagine ALL your components require globalize. This is like creating a global variable globalize, and who needs it — let it use it.
But the most elegant way is to use context. The Application component says “Hey, I have a globalize instance, if anyone needs it, let me know” and any lower component shouts “To me! I need him!". This is an elegant solution. The lower components remain clean, they do not depend on the store (yes, they depend on the context, but they must, because they need to be rendered correctly). An instance of globalize does not go through the entire hierarchy in props. Everyone is happy.