Dependency Injection in React — Part 2

Sergey Radzishevskii
3 min readJul 11, 2019

--

Let’s find some good solutions for a Dependency Injection Container.

in Dependency Injection part 1 we figured that we want to build a single context that could keep all required dependencies in one place. Essentially it will be a React Context for dependency injection container.

So we need to have a dependency injection container first. I was trying to surf npm and find some existing solutions. The most interesting and mature were

Autowiring as a killer feature

Of course, both of them can provide all basic functionality for DI container. But they also allow doing autowiring — when you can inject the object dependency implicitly.

Right now you don’t need to build DI config for a Client class. Autowiring is what I’m used to in other languages and what I have always used. But there’s a big BUT for me.

Other languages have types and they have reflection API. When DIContainer resolves a dependency in programming languages with built-in types it reads type using reflection and then finds registered dependency with this type.

Javascript does not have types. Autowiring won’t work for raw javascript. Typescript has types but it doesn’t have a built-in reflection. We have to use reflect-metadatain cooperation with decorators. So we have to update our tsconfig to

"experimentalDecorators": true,
"emitDecoratorMetadata": true

And we have to wrap every class with injectable decorator, it helps reflect-metadata actually gets meta info about types.

But wait…

Why does class Foo should know that it can be injected and it has a service that can be injected into it?

@injectable
class Foo {
constructor(@inject("Bar") private service: Bar) {}
}

Is it the responsibility of class Foo — NO. It shouldn’t know anything about the injection. It’s a trade-off for the autowiring. And that’s what I cannot accept personally.

Reinvent the wheel

If you want a thing done well, do it yourself.

Offtop

I don’t think that previous tools have a bad design for sure both are good tools. I just preferred to go my way. I speak in advance — use DI solution that you like. All of them has the pretty same functionality and it’s just a matter of taste. Just make sure that it has the following functionality.

// TS
const myService: IService = container.get<IService>("MyService")
// in JS
var myServicee = container.get("MyService")

So I decided to build my own solution. That won’t

  • depend on metadata
  • can work with raw Javascript

and I’ve built RSDIhttps://www.npmjs.com/package/rsdi

You need to build DI config first. rsdi provides helper functions for this — { value, object, factory }. All of them create definitions for DI container.

  • value is the simplest one it allows to get the raw value that was previously saved.
  • object will use class and instantiate a new instance. ObjectDefinition has construct function that can build arguments for the class constructor.
  • factory — is a function that will have the container as an argument. So we can resolve dependencies inside of the factory method.

Extra helper get allows referring to the existing dependencies. Therefore when we build arguments for the constructor of `AuthStorage`

“AuthStorage”: object(AuthStorage).construct(
get(“Storage”) // refer to dependency described above
),

And finally, the container itself has get method that will resolve a dependency for you.

const authStorage = container.get<AuthStorage>("AuthStorage");

How to inject from container to react component?

Now we have a solution for Dependency Injection Container but our final goal to inject the dependency into React component.

We will learn how to do that in part 3.

--

--

Sergey Radzishevskii
Sergey Radzishevskii

Written by Sergey Radzishevskii

Enjoy turning complex systems into intelligible architectures chilling in my garden.

No responses yet