With Dependency Injection you know how to open a door, but you have no idea who is behind it.

One of the challenging parts in software development is keeping code clean, maintainable and extendable. Robert C. Martin introduced five software design principles exposing the dependency management aspects of object oriented design known as SOLID. One of them is a Dependency Inversion principle that is saying “Depend on abstractions, not on concretions”. A pattern that implements this principle is called Dependency Injection (DI). This pattern makes it easier to decompose program components, write unit tests and even provision modules in run-time.

In React javascript library DI is done via Context. Let’s take a look at the Async Web Storage project in which I will apply React Context to use different mechanisms to store data.

Async Web Storage

This Web Storage will support three operations: find, upsert and remove.

Fig. 1. Abstract AsyncWebStorage.

A naive concrete implementation is an in-memory storage. But this idea could be extended to localStorage/sessionStorage, AWS S3, etc.

Fig. 2. Naive In-Memory Async Web Storage.

I will use React Context to provision an in-memory implementation by default. Both, in-memory and s3 storages will be exposed outside.

Fig. 3. React Context initialization with in-memory storage by default.

Let’s create a simple React component Foo that will read data from the storage. It will depend only on the abstraction injected via AsyncWebStorageContext (Fig. 4).

Fig. 4. Abstract Web Storage is injected via React Context.

Switching between storages is happening with AsyncWebStorageContext.Provider (Fig. 5).

Fig. 5. The first Foo component reads from memory, the second one — from s3.

Thus, we have visual component Foo and different Async Web Storage implementations, which are independent, can be developed in parallel and there are open for extension.