The Unstated React Service Pattern
This guide demonstrates a React Pattern for sharing global state, without requiring any of the following:
- 😒 Prop-Drilling
- 🔥 Redux
- 💲 MobX
- 🏀 Apollo Link State
(The above emojis were auto-selected by Emoj)
The Context API
Recently I found myself at an Advanced React Training with Michael Jackson (no… not the King of Pop, the King of React Router 😉). We spent a good deal of the time working through Higher Order components and the new React Context API.
Being so new to The React Way, (yet so familiar with frameworks like Angular), I was surprised that React didn’t ship with any built-in service architecture. React is a very different beast to other frameworks. It is intentionally designed to be all-state and no-service.
If you want to learn more about the React Context API, I recommend reading:
- Docs: The React Context API
- Blog: How to use the new React Context API
I found this blog post easier to understand than the examples in the React documentation — perhaps because I’m still on the React learning curve.
IMO: The fact that React is now shipping the Context API as a first-class citizen, means that a subscribing to global state (to use the term loosely), is no longer considered an anti-pattern. React is now providing a de-facto way of sharing state within the React tree without some of the limitations of higher order components.
“Unstated” Dependency Injection
But, the React Context API does not provide a method of dependency injection.
Dependency injection would allow us to instantiate multiple copies of a component with a discrete state that can be provided and consumed at any point in the app.
This pattern is useful for:
- Identical components that subscribe to different data sources with the same model, using the same methods and state properties
- Testing snapshots with mock states
Thankfully, there is “Unstated“— a tiny dependency that provides a handy wrapper around the Context API for dependency injection. I want to encourage you to read the Unstated documentation and get a feel for what it does, as I will be using it in the code examples below: Unstated README.md
Unstated Injection Demo
This CodeSandbox shows how to use Unstated to create multiple instantiations and inject them into separate providers of the same component.
Example: Creating a React Service with “Unstated”
So lets dig into things. My goal is make a reusable service module that I can inject into any part of the app. Our API Service must have the following requirements:
- Must be a self-contained re-usable module
- Can be Provided at any point in the React App
- Can be Subscribed to anywhere in the React App
(no prop-drilling necessary)
To keep things simple, we will mock out an API Service with the following methods and props.
boolean loggedIn
function logIn()
function logOut()
The Api.js
module might look something like this:
Now that we have our API service wrapped in module with Unstated, lets pull that into the top of our React App, inside of index.js
:
The next piece of the puzzle is subscribing to the API Service from deep within the React App. To make our example a little more realistic, I am going to subscribe to the API service from down in the React components that get loaded by the router.
Lets subscribe to our API Service inside of Pages/Home.js
:
After adding a few more Routes and subscribing to the same API Container, we have our pattern set up.
Take the the pattern for a quick spin in this CodeSandbox below. Test the following:
- Click Login:
state.loggedIn
prop gets updated in each route - Click Logout:
state.loggedIn
prop gets updated in each route - Click Login/Logout: from any route, the state is updated in all routes
Conclusion
Now we have a simple, re-usable React Service that uses the naturalized React Context API and the tiny Unstated module.
- No prop drilling required
- Dependency injection where needed
I’m going to call this the “The Unstated React Service Pattern” (unless another name already exists, or you have a better one 😀).
Disclaimer: I am new to React. Most of the apps I have built in the last four years have been Angular or Vue.js. If you are an experienced React dev and you see a better way to do this, kindly share the ❤️ and show us your approach in the comments.
Always ready to learn,
— Alistair MacDonald