React Dependency Injection
Published: 2019-10-01 18:24:00

Hey. I'm not that used to testing. But I do care about dependency injection. Because I understand that decoupling is a good thing.


So, consider these two cases:

**Funcional component** that depends on _imports_:

```javascript
import React from 'react';
import serviceA from './serviceA';
import serviceB from './serviceB';

function MyComponent() {
  // ...
  serviceA();
  serviceB();

  return <></>;
}
```

**Class component** that depends on _imports_:
```javascript
import React from 'react';
import serviceA from './serviceA';
import serviceB from './serviceB';

class MyComponent extends React.Component() {

  componentDidMount() {
    serviceA();
    serviceB();    
  }

  // ...

  render() {
    return <></>;
  }
}
```

A best approach would be to just pass all dependencies as `props`. Right?

```javascript
import React from 'react';

function MyComponent({serviceA, serviceB}) {
  // ...
  serviceA();
  serviceB();

  return <></>;
}
```

But still, I want to have _defaults_ to my services. I mean, I want my components to resolve their own dependencies when possible. So... back to dirty `imports`.


```javascript
import serviceA from './serviceA';
import serviceB from './serviceB';
```

Apart from _defaults_, sometimes you just don't want certain dependencies to come from the parent component. Because they are implementation details for a given task. For example: 

```javascript
import React, {Component, Fragment} from 'react';
import { isSingleWord } from "../helpers/selection";

class TextEditor extends Component { ... }
```

I wouldn't expect `isSingleWord` to be injected as a `prop`. Not everything is meant to be a `prop`.

However, `unit testing` should test just one thing, and `isSingleWord` should have it's own separated test. While `TextEditor` would be tested by mocking the `isSingleWord` dependency.

Test libraries like `Jest` let you change the import path of your dependencies _on the fly_. But that's too magical for me. What could be done then?

Well, working with functions, I use to set dependencies as default parameters, like this:

```javascript
import React from 'react';
import serviceA from './serviceA';
import serviceB from './serviceB';

function MyComponent({
  dependencyA: serviceA,
  dependencyB: serviceB,
}) {
  // ...
  dependencyA();
  dependencyB();

  return <></>;
}
```

That works with _functional components_ just fine. 

What about classes? I guess I could try the same trick in the constructor:

```javascript

class MyComponent extends React.Component() {

  constructor(props) {
    super(props);

    const {dependencyA, dependencyB} = props;

    this.dependencyA = dependencyA;
    this.dependencyB = dependencyB;    
  }

  //...
}
```

But that's too verbose, and maybe not that expressive. So, I'd propose to use a _HOC_. Like:

```javascript
import React from 'react';
import serviceA from './serviceA';
import serviceB from './serviceB';
import withDependencyInjection from './helpers/withDependencyInjection';

class MyComponent extends React.Component() {
  
  componentDidMount() {
    const {dependencyA, dependencyB} = this.props;

    dependencyA();
    dependencyB();    
  }

  // ...

  render() {
    return <></>;
  }
}

const dependencies = {
  dependencyA: serviceA,
  dependencyB: serviceB
};

export default withDependencyInjection(MyComponent, dependencies);

export { TestableComponent: MyComponent }; // you'd inject props yourself in test
```

So, with this _HOC_ you now have defaults for your props in classes.

```javascript
import React from 'react';

function withDependencyInjection(WrappedComponent, dependencies){
  return function(props) {
    return (
      <WrappedComponent
        {...props}
        {...dependencies}
      />
    )
  }
}

export default withDependencyInjection;
```

Let me know your thoughts.

¿Do you import dependencies directly in your components? 
¿Do you pass them as `props` instead? 
And ¿do you set _defaults_ for services when they come as `props`?

For testing, I guess the tendency to [mock imports _on the fly_](https://jestjs.io/docs/en/es6-class-mocks#the-4-ways-to-create-an-es6-class-mock) with **Jest** is really the way to go. I'm just sharing my thoughts.