To properly understand dependency injection in react, we have to first understand
- the concept of dependency injection in programming
- Why it exists?
- how dependency injection applies in javascript
- and finally, dependency injection in react
What is dependency injection
Let's tear the words apart and pick on dependency first - to be dependent means to be reliant on something.
If an object of class A uses some method from class B, then we say class A is dependent on class B, or class A has a dependency on class B
In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. It is a design pattern that allows the creation of dependent objects outside of a class and provides those objects to a class in different ways.
The Dependency Inversion principle encourages us to depend on abstractions instead of concretions. If you read separation of concerns, you are a bit familiar with how this works.
It is a form of inversion of control or a way of implementing inversion of control, which is also a software design pattern
Classes involved in a dependency injection
The Dependency Injection pattern involves 3 types of classes.
- Client Class: This is the receiving object
- Service Class: The object that is being injected
- Injector Class: The object that injects the service class into the client class.
Why should you care about dependency injection
- separation of concerns.
- Reusability — Services created outside of clients can be used everywhere
- Better testability — Makes code more maintainable and testable because dependencies’ impact can be removed by replacing dependencies with mocks or stubs
- Scalability — Easy to extend with new services as your application grows
- Allows concurrent or independent development
- Loosely coupled modules — This can be used as a software design tool to decrease the coupling between an object and its dependency
- Isolation — Helps isolate the client from the impact of design changes and defects
Dependency injection in javascript
In javascript, dependency injection looks like passing in an object the client needs as arguments.
Let's look at a user service that creates a user object and stores the object in the users' array
// ./userService
const userServices = {
users: []
createUser({ name }){
const user = {
id: this.users.length + 1,
name
}
this.users.push(user);
return user
}
}
Next, we have a user controller function that calls the user service
function createUser({ name }){
const user = userServices.createUser({ name })
return { user }
}
To make the user controller function independent of the service, the userServices can be injected into the function as an argument
function createUser({ name }){
const user = userServices.createUser({ name })
return { user }
}
Dependency injection in react
How does dependency injection work in react applications?
React in fact has inbuilt support for dependency injections. However, we may not necessarily think of them as dependency injections.
Via props
Props are a way to pass dependency or a list of dependencies in our application
function welcome(props) {
return <h1> Hello, {props.name}</h1>;
}
Via context
Context is another method for dependency injection. Having context can be redefined and extracted using hooks at any level of our components.
function counter() {
const { name } = useContext(MessageContext);
return <p>{ name }</p>;
}
Via JSX
JSX is also a method supported by React for dependency injection. Nested components would often have a parent-child relationship, in the above example which is a form of dependency injection.
<Navbar>
<Header />
<SocialMediaLinks />
</Navbar>
Drawbacks
- It can require you to write more lines of codes
- Might be confusing to get started with
- React has inbuilt dependency injection, but for a large-scale app, you might need to use an external library such as inversify-react which might be an additional overhead
🍵 Happy coding!