Advanced Dependency Injection in React
By: Minura Samaranayake
Tags: Web Development, JavaScript, React, Dependencies, Dependency Injection

Advanced Dependency Injection in React

Dependency injection is a pattern that reduces hardcoded dependencies. It promotes composability by enabling one dependency to be replaced by another of the same type.

This article will evaluate the dependency injection support in React and how we can extend it using InversifyJS.

Why Dependency Injection?

Dependency Injection (or, more broadly, inversion of control) is used to address several challenges.

Loosely coupled modules — This can be used as a software design tool, forcing to keep code modules separate.

— This can be used as a software design tool, forcing to keep code modules separate. Better reusability — This makes it easier to substitute modules of the same type.

— This makes it easier to substitute modules of the same type. Better testability — Makes it easily testable by injecting mock dependencies.

Besides, when it comes to React, it has inbuilt support for dependency injection.

Dependency Injection with React

However, we may not necessarily think of it as dependency injection. Let’s take a look at few examples to understand them better.

1. Using props allows dependency injection.

function welcome(props) {

return <h1> Hello, {props.name}</h1>;

}

The welcome component receives the props parameter name and produces an HTML output.

2. Using context is another method for dependency injection.

function counter() {

const { message } = useContext(MessageContext);

return <p>{ message }</p>;

}

Since context is passed down the component tree, we can extract it using hooks inside components.

3. JSX is also a method supported by React for dependency injection.

const ReviewList = props => (

<List resource="/reviews" perPage={50} {...props}>

<Datagrid rowClick="edit">

<DateField source="date" />

<CustomerField source="customer_id" />

<ProductField source="product_id" />

<StatusField source="status" />

</Datagrid>

</List>

);

The perPage parameter is passed to the <List> component, which fetches the “/reviews” route from the REST API.

However, <List> does not directly render the reviews. Instead, it delegates to the child component <Datagrid> , which renders the list as a table.

This signifies that the rendering of <List> is dependent on the <Datagrid> . And it’s the caller of <List> that sets this dependency.

So the parent-child relationship, in the above example, is a form of dependency injection.

However, these strategies may be helpful for a small application. But if it scales, you need better support for dependency injection beyond these basics.

So let’s look at few libraries that extend the dependency injection support for React.

Extend using InversifyJS

InversifyJS is a JavaScript dependency injection library that is powerful, lightweight, and simple to use. However, using it with React as a component feature is still challenging.

This is because InversifyJS uses constructor injection, whereas React does not allow users to extend the constructors of its components. However, let’s look at how we can solve this challenge with an example.

If you take a closer look, the IoC initialization and the NameProvider class return a name displayed by the React Component. And do you know that it’s a typical InversifyJS use case?

This will create a new instance of the class for every injection as a Transient scope declaration.

Besides, Singleton scope (the same instance for every injection) and Request scope (the same instance for one container.get ) are also available.

However, let’s look at few InversifyJS extension libraries we can use to expand its behavior.

1. Using inversify-inject-decorators

When we can’t perform a constructor injection, this is the preferred technique to use with InversifyJS. This library has four additional decorators to be used in different projects, namely:

lazyInject

lazyInjectNamed

lazyInjectTagged

lazyMultiInject

This gives a lazily evaluated injection. It means that the dependence isn’t provided while the object is initialized. Instead, it is provided at its first usage and cached for later.

During the initialization, a correct Boolean value can be used to switch off caching. Besides, you can also go through the complete example by using the Inversify-Inject-Decorators link.

2. Using inversify-react

Inversify-react is a library that uniquely performs dependency injection. By using it, we get a React component provider for our use. This provider holds the IoC container and passes it down in the React tree.

Besides, we also get four decorators;

provide

provide.singleton

provide.transient

resolve

And the entire code example is given in Inversify-react-example.

3. Using react-inversify

Although the name is similar to that of the previous library, this one takes a very different approach. Here, we have a provider component that is comparable to inversify-react.

Since we need to construct an additional class and wrap our component in a higher-order component, usage is somewhat more challenging.

However, this approach is closer to React because objects are passed as properties rather than instantiated inside the component.

Another benefit is that we can make full use of InversifyJS. That means we don’t have to rely on what the library has to provide.

However, it does require writing a lot more code than other solutions. Besides, you can access the complete example via the React-inversify-example link.

Conclusion

Dependency Injection is being used by many popular libraries in the React ecosystem, such as React Router and Redux. Additionally, React also has direct support for dependency injection.

However, for advanced use cases, we need libraries such as InversifyJS.

Thank you for reading. At last, if you have any clarifications, feel free to comment down below.