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
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
withHigherOrderComponent(MyComponent). I wanted
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:
Now, they do this:
(ReactNContext._currentValue2 || singletonGlobalState)
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.
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.