Context is a new feature released in the official API with React 16.3.0. Very basic at first glance, it reveals its full potential very easily with a bit of imagination.
I experiment the Context feature in React since its first official release 16.3 and now with new release 16.6 I definitively cannot do without it anymore. I already wrote several libraries using this feature and the context always simplify the communication between components.
I will try to explain you in this article how you could use Context in your own React application.
Context feature is nothing more than a way to inject dependencies into a component without using properties.
Sometimes, you want to share data between several related components like theme or the width of the labels in a form. With React, you previously had 2 solutions.
The first way was to transmit your data from parent to children using properties. By this way, your data is defined in the state of your root component and is propagated to children components using the properties.
However it was frustrating to propage it several level below because you needed to create the property for each of your components. It was even more frustrating when only few components needed the data like the leafs on the virtual DOM.
On another hand, you could also use the redux to store this data and then create a container only for the components that really needed the data.
The challenge with this second option is the propagation of different values depending on the position of your component in the DOM.
Now Context is there!
Context uses 2 components: a provider and a consumer. The provider defines the data it wants to propagate. The consumer get access to data defined by closest ancestors provider (not only direct parent component). If there is no provider on the top of a consumer, default values are provided instead.
With context, you can propagate information to children components, grand children components and other descendant components. You can create several times the same context with different values, each component will get the data depending on its position in the DOM.
A first use case you often see to use context is a color theme. With context, you can easily define a theme in the root component and so all components can have access to the theme. But we can go further.
In order to add custom behavior/design, you sometimes create your own components Form, Label, Input, Select that are wrappers around default html element form, label, input, select.
In this example, each label has a class name to specify the width of the label and a label to display a text before the input.
With a context, you can allow these components to interact more efficiently together. For example, the Form component could provide a context to indicate itself which class name all children labels should consume. Or the input could automatically consume the label provided to the Label if no placeholder is given.
Using context, you can create a lot of smart interactions between your own components. And the very useful thing is that components don’t need to be direct children, a lot of other components or html elements may be between the provider and the consumer.
This may save you a lot of code but the best part is coming!
Top down or bottom up
The strategy explained above is based on a from top to down transfer of data. The parent can propagate information to the children. But you can also use context to have a bi-directionnal communication.
As soon as I saw context, I imagined a library to make my components appear progressively. Using a counter provided by a parent provider, each children was able to detect when it was its turn to appear.
It was a first good step. I didn’t need to have all the animated items in the same level of the DOM.
But, in order to stop the increment after the last component, I had to define the number of items in the AnimatedGroup because the parent don’t known the number of animated items it needs to manage — remember that all animated items are not direct children. I could also let it increase indefinitely but it was resource consuming. Besides, I had to define the index of each AnimatedItem to known when it need to appear.
In version 16.6, React released a new upgrade which allows the context to be used in all methods of a component and not only the render method.
In the same time, the context provider can transfer data but also callbacks.
With callbacks and access to the context everywhere, you can now create bottom up communication. In the previous use case, AnimatedGroup could provide a function register. Each AnimatedItem would just need to call this method to be counted by AnimatedGroup and get in return an index.
So that at the end, the interaction between AnimatedGroup and AnimatedItem becomes:
You don’t need anymore to define the itemNumber since with the register callback provided the context, AnimatedGroup knows the number of AnimatedItem mounted. In the same time the register has returned a index for each AnimatedItem mounted.
The code is lighter and the interaction between AnimatedGroup and AnimatedItem is smarter.
And it works so well I really published the library. The example on the animation library is just a little example to show that Context can dramatically automate the interaction between components you create. The power of context has for only limit your imagination!
You could for example use a second callback to notice the provider when the animation of a AnimatedItem is finished and so animate a component only when the previous one is completely displayed.
I hope that this article about Context gave you some ideas about how to use it. I can ensure that once you will try it, you won’t be able to do without it anymore too.
If you have any question, do not hesitate to post it below. And don’t forget clap the hands if this article helped you.