How ReactN hacks React Context

Getting Context value without using Context

Some of you may be familiar with ReactN, a boilerplate-free global state management solution for React applications. In an attempt to solve common complaints by developers about other global state packages, the driving design principle behind ReactN is “What if global state were built into React?” All features attempt to mimic React’s design decisions, giving the illusion they are a part of the React core.

This article covers an edge case feature implementation. The way this feature is implemented is unlikely to ever be beneficial to you personally. The purpose of this article is to share what was learned, offer eager developers insight into how a complex problem was solved, discuss design decisions, and engage the community in discussion around those decisions.

What’s the feature? 🐛

Last weekend, ReactN launched 1.0, denoted by the highly demanded feature of Context support. Developers who were concurrently rendering their applications had the issue that the global state was shared across each instance. Now, a <ContextProvider> can wrap any or all of a React application, and a new instance of a global state is limited to the scope of that Context. At the same time, developers can now use Provider1.useGlobal and Provider2.useGlobal as Hooks to access multiple global states in a single functional Component.

The design for this feature became difficult, as requiring a React Context is not boilerplate-free and does not feel “built into” React. ReactN components needed to support this feature, but fallback to a global state if no Context was provided.

What’s the problem? 🤷

For functional components, there was no problem. They can already useContext to check for as many Contexts as they want.

For class components, this was particularly tricky. Class components can only subscribe to one Context, unless the user adds boilerplate. If ReactN Components used static contextType to subscribe to the ReactN Context, then all components that extend ReactN.Component could not subscribe to a Context of their choice. In normal, best practice React design, this would be handled by a higher-order component that subscribes to the ReactN Context and then passes the global state as a prop to the user-defined Component. This is one of the reasons why other global state solutions have so much boilerplate — each feature is an additional nested component. This isn’t bad design per se, but the developer experience suffers, making it not a suitable solution for ReactN. It does not feel “built in” to React to have to use this.props.globalState and withHigherOrderComponent(MyComponent). I wanted this.global and MyComponent, I wanted global components that could still subscribe to a Context of their choice, and I wasn’t taking no for an answer.

What’s the solution? 🤔

Since the global components are in the same package as the global state Context, they have access to that Context as a component. The question was, “Can I get the value of a React Context outside of static contextType or useContext?” So I dug into the React source code.

I did a GitHub search for org:facebook repo:react contextType in an attempt to find where it is processed. Unfortunately, the react package itself only checks contextType for errors instead of processing it. However, the CHANGELOG.md did mention the addition of contextType and linked a pull request. Surely, this pull request would contain relevant code. I found that the magic function to convert contextType to value is a function called readContext. Therein, I find that it’s really quite simple: Context._currentValue2.

How’s it implemented? 💡

All global methods on class components previously did this:

methodName(...params) {
singletonGlobalState
.doMethodThing(...params);
}

Now, they do this:

methodName(...params) {
(ReactNContext._currentValue2 || singletonGlobalState)
.doMethodThing(...params);
}

If a ReactN Context surrounds it, a class component will use the global state instance it provides. Otherwise, it falls back to the singleton global state.

What are the caveats? 🙃

There is one very important reason why I say that this likely won’t benefit others. By reading a Context’s value via _currentValue2, the component is not subscribing to the Context changes. In a normal React Context, the value is meant to update. In a ReactN Context, the global state instance is constant. ReactN Providers do not accept values. The values on the state may change, but the global state instance itself triggers re-renders on the Components, not by Context change.

If your Component reads Context value by this method, beware that it will not re-render when the Context changes. This is not a problem for ReactN, but it is likely unusable for other Context use cases.

Conclusion 🔚

If you liked this article, feel free to give it a clap or two. It’s quick, it’s easy, and it’s free! If you have any questions or relevant commentary, please leave them in the comment section below.

To read more of my columns, you may follow me on LinkedIn and Twitter, or check out my portfolio on CharlesStover.com.

Senior front end engineer / charlesstover.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store