Detailed explanation of the new React context

Written by csssr | Published 2018/09/24
Tech Story Tags: react | javascript | front-end-development | development

TLDRvia the TL;DR App

By artalar

What is context

In addition to props, which are accessible only to the immediate child component, React components can define a context, which is accessible to all of their nested children. That means that no matter how deeply nested a component is, it can access context created any number of nodes up the tree. Context API is the base for all of the popular libraries that require the ability to access global values from each level of the component tree: react-redux, react-mobx, react-router, styled-components (ThemeProvider).

Issues with the old context

In the old version data could be received, but it was impossible to subscribe to its updates. To be exact, update — as it goes for props — occurred when the parent component re-rendered. But since the data is being transferred from the context source to the context consumer through the large (in fact, any) number of components, and those can stop the update by their own shouldComponentUpdate, this kind of subscription cannot be called reliable. So while designing libraries, where data should be updated for consuming components, developers had to create update mechanisms manually — which is not an easy job as a practical matter. Read more in the article or watch the presentation of MobX creator.

New API React.createContext

Some developers consider the new context ([React.createContext](https://reactjs.org/docs/context.html)) to be a replacement of redux (or any other state container), but that is comparing apples to oranges. The main objective of the new context is to facilitate effective updates of consumers (more details by Dan Abramov) so that the developers can focus on the main features of their libraries. The new context also boasts an improved, more intuitive interface.

Note that redux has minimal functionality for managing the states: subscription and update. Those can be substituted with using state (and setState) from the regular React.Component. It would be more accurate to say that the new context, in some applications, can be used instead of redux, meaning that the React.Component state and update can be used instead, and react-redux can be replaced by React.createContext. There isn’t an “out-of-the-box” replacement of redux middleware with context, but there are third party libraries.

render-prop

As you can see, the new React.createContext API uses the render-prop approach to share the state or behaviour with the other subscribed components. While the details of implementation and example code are outlined in the official documentation, I would like to highlight the benefits and drawbacks of this method:

  • (+) Prevention of naming collisions in case of multiple subscriptions (Consumer). Because the classic HOCs combine props, if we have multiple HOCs in a row, and any of the passed properties have identical names, the values will be overwritten, and the final props object that reaches the component will have the value of the very last HOC. Using render-prop solves that problem, because props are not combined, and each passed parameter of the subscription is accessed individually within the passed function.
  • (–) “Сallback hell” and generation of new functions, or the alternative of redefining the render portions into separate methods (which interferes with the consistency of design pattern). More details are in the official documentation.

If you don’t like the render-prop approach, and you would rather use the “good old” HOCs — here is a simple example of how it can be implemented with memoization:

unstable_observedBits

Information found in source code and tests of React, and in this article

While it has not been publicly announced or added to official documentation (and most likely, won’t be), in addition to previously mentioned functionality, React.createContext has a second argument that accepts a function. The Consumer component also accepts a bitmask as unstable_observedBits prop. This functionality is similar to shouldComponentUpdate in React.Component. Let’s take a closer look.

Bit masks

Bit masks are an old technology, used in particular in Linux permissions settings. The concept of bit masking involves each bit, in specific order, setting true or false indicators for a specific setting. The advantage of bit masks is that to update the value, only a single bitwise operation needs to be performed on the original bit mask using a rule mask. To set the value of the target bit to true, an “OR” — | — bit operator is used with the target bit === 1, and other bits in the mask set to 0. To set the value to false, an “AND” — & — bit operator is used with the target bit === 0, and other bits in the mask set to 1. This may seem confusing in the beginning, but in practice it is a simple, intuitive, and fast method of storing and modifying true/false values.

Usage

The bit mask in the description and examples below is used to track changes in the state. Each bit in the bit mask should correspond to a value in the state.

The second argument of React.createContext accepts a function that takes the previous and new states, and returns the updated bitmask. In turn Consumer accepts a bitmask containing the bits of the “true” values responsible for the state values we want to observe, as unstable_observedBits. When Consumer receives the bitmask, it compares the new bitmask with unstable_observedBits, and performs a bitwise AND operation on them. The Consumer will only be re-rendered if the result of the operation is not 0. If the second argument of React.createContext and the unstable_observedBits Consumer argument are not provided, render-prop will be called any time the context changes.

Example

As indicated by the parameter name, this API is not stable and should not be used in production.

create-subscription

Another utility added to the React source code is [create-subscription](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#adding-event-listeners-or-subscriptions). Previously, to manage subscriptions, respond to property changes and re-render components developers had to create a wrapper using React.Component, which would trigger setState or forceUpdate. To simplify subscriptions, developers can now use the more transparent createSubscription API from the create-subscription package in the official React repository.

Summary

React 16.3 brings many interesting changes and will, without a doubt, improve the quality of use of the React framework and simplify the development of React support libraries.

Use of the new context can be viewed in this interactive demo:

All of the features above can be viewed in this interactive demo:


Published by HackerNoon on 2018/09/24