Injectable services in React
How they’re implemented and their similarities with Angular services
React provides a fantastic API for building components. It’s light-weight and intuitive, and became a sensation in the dev community for a reason. With the introduction of the most recent API features: hooks and context/provider, components have became not only more functional, but also more testable. Let me explain.
So far, when we wanted a component to use an external service, we would simply implement it in a separate module, import it, and use its exported methods, like so:
Keep in mind that this is NOT how I would actually write my code in production, there’s no error handling, and both components are defined under a single module which I don’t see as a good practice, but for demonstration purposes it’s more than enough.
The components above would work well within a React app, because essentially they can achieve what they were implemented for. However, if we would like to unit-test these components, we would encounter a problem, because the only way to test these components would be via e2e tests, or by completely mocking the fetch API. Either way, the solutions are not in our favor. Either we completely overkill it with testing, or we make use of a not-so-simple mocking solution for an ENTIRE native API. Below is an example:
If so, how does one suppose to overcome this problem?
Let’s learn from our Angular fellows
I know what you’re probably thinking right now… What is this guy thinking, promoting Angular design patterns which are completely no match for the great React. First of all, React is not perfect, and always has places for improvements. If it was already perfect, they wouldn’t have kept working on it on Facebook. Second, I like React, and I believe in it very much, this is why I would like to make it better by ensuring best practices. So before you close your tab in anger please continue reading and listen to what I have to say :-)
In the Angular team, they came up with a clever approach. Instead of relying on hard-coded imports, what they did they provided a mechanism that would let us inject our services before we initialize the component. With that approach, we can easily mock-up our services, because with the injection system it’s very easy to control what implementation of the services is it gonna use. So this is how it would practically look like:
And now if we would like to test it, all we have to do is to replace the injected service, like mentioned earlier:
To put things simple, I’ve created a diagram that describes the flow:
Applying the same design pattern in React
Now that we’re familiar with the design pattern, thanks to Angular, let’s see how we can achieve the same thing in React using its API. Let’s briefly revisit React’s context API:
The context can be seen as the container that holds our service, aka the
value prop, as we can see in the example above. The provider defines what
value the context will hold, so when we consume it, we will be provided with it. This API is the key for a mockable test unit in React, because the
value can be replaced with whatever we want. Accordingly, we will wrap our
And we will update our component to use the new
useAuth() hook uses the context API under the hood, it can be easily replaced with a different value. All we have to do is to tell the provider to store a different value under its belonging context. Once we use the context, the received value should be the same one that was defined by the provider:
One might ask: “Does this mean that I need to wrap each and every service with the context API?”, And my answer is: “If you’re looking to deliver an enterprise quality React app, then yes”. Unlike Angular, React is more loose, and doesn’t force this design pattern, so you can actually use what works best for you.
Before I finish this article, here are some few things that I would like to see from the community, that I believe will make this work flow a lot easier:
- Have a 3rd party library that would wrap a service with the context API and would simplify it.
- Have an ESLint rule that will force the usage of injectable React services.
What do you think? Do you agree with the design pattern or not? Are you gonna be one of the early adopters? Write your thoughts in the comments section below. Also feel free to follow me on Medium, or alternatively you can follow me on: