July 27, 2016

Most of the solutions for dependency injection (DI) in React components are based on context. The famous connect function and the Provider there use the context. However, the React development team recommends avoiding the usage of the context. In this article we will learn how we can use InversifyJS as a context-free solution for DI in React applications. #

A few weeks ago, an user in Twitter asked Michel Weststrate (the author of MobX) the following:

His answer was the following:

Later, the popular repository react-in-patterns documented the most common dependency injection patterns in React applications but some developers mentioned InversifyJS once more:

I was surprised after reading the following in react-in-patterns:

Most of the solutions for dependency injection in React components are based on context. I think that it’s good to know what happens under the hood. As the time of this writing one of the most popular ways for building React apps involves Redux. The famous connect function and the Provider there use the context.

I was surprised because, based on the React docs, we should try to avoid using context:

Context is an advanced and experimental feature. The API is likely to change in future releases.

Most applications will never need to use context. Especially if you are just getting started with React, you likely do not want to use context. Using context will make your code harder to understand because it makes the data flow less clear. It is similar to using global variables to pass state through your application. If you have to use context, use it sparingly. Regardless of whether you’re building an application or a library, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it’s easier to upgrade when the API changes.

In this article we are going to learn how we can use an IoC container to inject a value into React components without passing it explicitly through each component and without using the React context.

We are going to use InversifyJS as IoC container and TypeScript as programming language. We are using InversifyJS because it works in both Node.js and web browsers. This is an important feature because some React applications use server-side rendering. We are also using TypeScript because it is the recommended by InversifyJS.

About the integration of IoC containers with React #

InversifyJS supports two kinds of injections:

Constructor injection

Property injection

In order to use “constructor injection” the IoC container needs to be able to create the instances of the classes. In React the components sometimes are just functions (not classes) and we can’t delegate the creation of the instances of the components to the IoC container. This means that constructor injection powered by IoC containers don’t play nicely with React

However, property injection works nicely if what we want is to pass dependencies to components without passing them explicitly through each component. Let’s take a look to a basic example.

Show me the code! #

We need to start by configuring the IoC container. In InversifyJs we need to create a dictionary that maps a type identifier with a type. The dictionary entries are known as “type bindings”.

In this case, we use a binding to map the identifier UserStore to the class UserStore . This time the identifier is the Class but InversifyJS also allow you to use Symbols or string literals as identifiers. Symbols or string literals are required when we use interfaces.

import { Kernel, makePropertyInjectDecorator } from "inversify"; import { UserStore } from "./store/user"; import "reflect-metadata"; let kernel = new Kernel(); kernel.bind<UserStore>(UserStore).to(UserStore); let pInject = makePropertyInjectDecorator(kernel); export { kernel, pInject };

We also need to generate a decorator using the function makePropertyInjectDecorator .

The generated pInject decorator allow us to flag the properties of a class that we want to be injected:

import { pInject } from "./utils/di"; import { UserStore } from "./store/user"; class User extends React.Component<any, any> { @pInject(UserStore) private userStore: UserStore; public render() { return ( <h1>{this.userStore.pageTitle}</h1> ); } }

Injected properties are lazy evaluated. This means that the value of the userStore property is only set after we try to access it for the first time.

Please note that in the future releases of InversifyJS the function makePropertyInjectDecorator will be removed. It will be provided by its own module: inversify-inject-decorators.

The lazyInject decorator is the equivalent to the previously known as pInject decorator.

import getDecorators from "inversify-inject-decorators"; import { Kernel } from "inversify"; import { UserStore } from "./store/user"; import "reflect-metadata"; let kernel = new Kernel(); kernel.bind<UserStore>(UserStore).to(UserStore); let { lazyInject, lazyInjectNamed, lazyInjectTagged, lazyMultiInject } = getDecorators(kernel); export { kernel, lazyInject, lazyInjectNamed, lazyInjectTagged, lazyMultiInject };

The main advantage of using an IoC container like InversifyJS is that we are not using the context!

InversifyJS is also great for testing because you can declare a new binding to inject a mock or stub instead of a real value:

kernel.bind<UserStore>(UserStore).toConstantValue({ pageTitle: "Some text for testing..." });

You can find some real use cases of InversifyJS with React here and here. You can learn more about InversifyJS here.

Please feel free to share thoughts about this article with us via @OweR_ReLoaDeD, @InversifyJS and @WolkSoftwareLtd.

Don’t forget to subscribe if you don’t want to miss it out future articles!

247 Kudos