No Need for Dependency Injection in React Components

Dependency Injection (DI) is a big part of the way things are done in Angular, so it’s natural to look at whether/how it’s needed in building UI components with React or similar tools.

If you’re not familiar with dependency injection or why anyone would want it in their UI tools, see the Angular Docs and example code. My working definition is “Dependency injection is a way to simulate adding more parameters to a function.” The goal of dependency injection is to make components more reusable and testable.

I’ll discuss three main use cases for DI in building UI components and show how to solve these problems in React without DI:

  • Putting Business Logic in a Component
  • Putting API-Calling logic in a Component
  • Putting other Components in a Component

How to Put Business Logic in a Component

Don’t do it. See https://en.wikipedia.org/wiki/Separation_of_concerns#Origin.

How to put API-Calling Logic in a Component

I avoid putting data fetching and submitting logic directly into components. Instead, I have the component accept data-fetching functions as props. For example, this component accepts a `onSubmit` prop and uses it to submit data:

What’s nice about passing props:

  • The function for submitting data can be tested separately and used in other components (like in DI).
  • The component can be tested and used without a specific `handleSubmit` function. The function used in the test can be something like `() => test.pass()`.

Putting Other Components in a Component

Part of what makes UI components fun is that they are composable—we can make new components by putting together existing components. For example, I added a <Checkbox /> element to the <TodoForm />:

Tests probably aren’t necessary for stateless, logic-less components. But if you must unit-test <TodoForm />, you may want to do so independently of the implementation details of <Checkbox />. That’s where shallow rendering helps.

“Normal” React rendering produces virtual DOM nodes, which represent things at the very bottom of the view hierarchy, such as <div />, <span />, <input /> etc. “Shallow rendering” is rendering just one level deep, which in this example includes a Checkbox.

This is a test that uses Enzyme’s shallow rendering helpers to confirm that <TodoForm /> renders a <Checkbox /> with the right `props`.

Conclusion

  • When a component depends on a function, you can pass the function in as a `prop`.
  • When a component depends on another component, shallow rendering can help keep your unit tests isolated.
  • No dependency injection library or framework is needed for reusable, testable UI components.