When and Why you should do Dependency Injection in React 
Tags: react, webdev, javascript, architecture
Published: 2019-11-07 22:03:43

Our React applications are composed of many small components or modules. The components that we write will sometimes be dependent on each other. As our application grows in size a proper management of these dependencies among components becomes necessary. Dependency Injection is a popular pattern that is used to solve this problem.

In this article we will discuss 
   * When is it necessary to apply dependency injection pattern 
   * Dependency injection with Higher Order Components (HOC)
   * Dependency injection with React Context

> Note: If you have no prior knowledge of dependency injection, I would recommend the following [blog post] (https://khalilstemmler.com/articles/tutorials/dependency-injection-inversion-explained/)

Let’s consider the following example.

```jsx
// app.js
function App() {
  const [webSocketService, setwebSocketServicet] = React.useState({});
  React.useEffect(() => {
    // initialize service
    setwebSocketServicet({
      user: `some user`,
      apiKey: `some string`,
      doStuff: () => console.log("doing some function")
    });
  }, []);
  return (
    <div>
      <B socket={webSocketService} />
    </div>
  );
}
```
Here we have our `App` component that is initializing a service, and passing the reference as a props to its children. 

```jsx
// B.js
function B(props) {
  return (
    <div>
      <A {...props} />
    </div>
  );
}

// A.js
function A(props) {
  // Do something with web socket
  const doWebSocket = () => {
    props.socket.doStuff();
  };
  return (
    <div>
      <button onClick={() => doWebSocket()}>Click me</button>
      {props.children}
    </div>
  );
}
```
Component `B` receives the props from `App` and passes it down to `A`. `B` doesn't do anything with the passed props. Our `websocket` instance should somehow reach the `A` component where it is being used. This is a very basic example application but in a real world scenario when we have lots of components nested inside one another we have to pass this property down all the way. For instance 

```jsx
<ExampleComponentA someProp={someProp}>
  <X someProp={someProp}>
    <Y someProp={someProp}>
      //.... more nesting 
      //... finally Z will use that prop
      <Z someProp={someProp} /> 
    </Y>
  </X>
</ExampleComponentA>

```

Lots of these components are acting as proxy in passing this prop to their children. This is also making our code less testable, because when we write tests for these components (X or Y) we have to mock `someProp` even though the only purpose of that property is to pass it down the children tree.

Now let's see how we can solve this problem with a Dependency Injection using a Higher Order Component.

Let’s create a file called `deps.js` and inside the file we will have two functions

```jsx
import React from "react";

let dependencies = {};

export function register(key, dependency) {
  dependencies[key] = dependency;
}

export function fetch(key) {
  if (dependencies[key]) return dependencies[key];
  console.log(`"${key} is not registered as dependency.`);
}
```

Here in the `dependencies` object we will store names and values of all our dependencies. The `register` function simply registers a dependency and `fetch` function fetches a dependency given a key.

Now we are going to create a HOC that returns a composed component with our injected properties.

```jsx
export function wire(Component, deps, mapper) {
  return class Injector extends React.Component {
    constructor(props) {
      super(props);
      this._resolvedDependencies = mapper(...deps.map(fetch));
    }
    render() {
      return (
        <Component
          {...this.state}
          {...this.props}
          {...this._resolvedDependencies}
        />
      );
    }
  };
}
```

In our `wire` function we pass a `Component`, an array of `dependencies` and a `mapper` object and it returns a new `Injected` component with the dependencies as props. We are looking for the dependencies and mapping them in our constructor. We can also do this in a `lifecycle` hook but for now let’s stick with constructor for simplicity.

Alright, let’s go back to our first example. We will be making the following changes to our `App` component 

```diff
+ import { register } from "./dep";

function App() {
  const [webSocketService, setwebSocketServicet] = React.useState(null);
  React.useEffect(() => {
    setwebSocketServicet({
      user: `some user`,
      apiKey: `some string`,
      doStuff: () => console.log("doing some function")
    });
  }, [webSocketService]);
+ if(webSocketService) {
+   register("socket", webSocketService);
+   return <B />;
+ } else {
+   return <div>Loading...</div>;
+ }
}
```

We initialized our WebSocket service and registered it with the `register` function.  Now in our `A` component we do the following changes to wire it up.

```diff
+const GenericA = props => {
+  return (
+    <button onClick={() => console.log("---->>", +props.socket.doStuff())}>
+      Push me
+    </button>
+  );
+};
+const A = wire(GenericA, ["socket"], socket => ({ socket }));
```

That's it. Now we don't have to worry about proxy passing. There’s also another **added benefit** of doing all this. The typical module system in JavaScript has a caching mechanism. 

> Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.

> Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

***taken from node.js documentation

What this means is that we can initialize our dependencies and it will be cached and we can inject it in multiple places without loading it again. We are creating a ***Singleton*** when we are exporting this module.


But this is 2019 and we want to use context api right ? Alright, so let’s take a look how we can do a dependency injection with React Context.

> Note: If you would like to know more about how other SOLID principles apply to React. Check out my previous post [here](https://dev.to/shadid12/can-you-apply-solid-principles-to-your-react-applications-46il)

Let’s create a file called `context.js`

```jsx

import { createContext } from "react";

const Context = createContext({});

export const Provider = Context.Provider;
export const Consumer = Context.Consumer;

```
Now in our `App` component instead of using the register function we can use a Context Provider. So let’s make the changes

```diff
+import { Provider } from './context';

function App() {
  const [webSocketService, setwebSocketServicet] = React.useState(null);
  React.useEffect(() => {
    setwebSocketServicet({
      user: `some user`,
      apiKey: `some string`,
      doStuff: () => console.log("doing some function")
    });
  }, []);

  if (webSocketService) {
+    const context = { socket: webSocketService };
    return (
+      <Provider value={ context }>
        <B />
+      </Provider>
    )
  } else {
    return <div>Loading...</div>;
  }
}
```
And now in our `A` component instead of wiring up a HOC we just use a Context Consumer.


```jsx
function A(props) {
  return (
    <Consumer>
      {({ socket }) => (
        <button onClick={() => console.log(socket.doStuff())}>Click me</button>
      )}
    </Consumer>
  );
}
```
There you go and that's how we do dependency injection with React Context.



### Final Thoughts

Dependency Injection is being used by many React libraries. React Router and Redux are the notable ones. **DI** is a tough problem in the JavaScript world. Learning about these techniques not only makes one a better JavaScript developer but also makes us critically think about our choices while building large applications. I hope you liked this article. Please follow me and spare some likes ;)

Until next time. 

*** NOTE: This post is a work in progress, I am continuously updating the content. So any feedback you can provide would be much appreciated ***