Globally Shared Instances in Angular — Use with Care
Tags: angular, webdev
Published: 2024-02-06 11:52:22


Why you should use `{providedIn: “root”}` not always, not by default, but only when you need it and know how to use it.

### Why do we need the `Injectable()` decorator?

To let the Angular Dependency Injection mechanism inject it. So by definition, if you decorate your class with the `Injectable()`, you want to later use it in your components (or other services), and let Angular DI take care of the dependencies of this decorated class.

There is one configurable option: `providedIn`, which currently has only 2 meaningful values: `root` and `undefined` (default).

Services with `providedIn: root` will be instantiated just once. Without this option — will be instantiated every time, when a component that has them in its `providers` array is instantiated. That is an important difference.

### Why ‘use with care’?

Services, `providedIn: root` will be shared across your application. It means, that every component, every service that injects such services, will get the same instance. It is not “bad”, or “wrong”, it just means that you should write the code in such services with constant consideration, that multiple components will use it at the same time.

Sometimes it is exactly what you need! For example, `HttpClient` with its interceptors. You want to configure them once and use the same instance of `HttpClient` across the whole application.

But quite often developers just add it because tutorials told them to, and they don’t know other ways of injecting a service. And not every time developers are actually aware of the fact that multiple components will share the same instance of the service they create.

Here is a **modified** example from Angular [documentation](https://angular.dev/essentials/sharing-logic):
```ts
    import {Injectable} from '@angular/core';
    @Injectable({
      providedIn: 'root',
    })
    export class CalculatorService {
      scale = 1;
    
      add(x: number, y: number) {
        return (x + y) * this.scale;
      }
    }
```
If some component modifies the scale field, all other components that use this service will be affected. It is a simplified example, in the real code things are more sophisticated.

### How to use it correctly?

The main rule of globally shared services: they should either not have an internal state, or their state should not be modifiable after instantiation, or their code should be aware that multiple different consumers will modify this state in a chaotic order (that’s why methods that modify the state should be atomic — only one call should be expected from a consumer, not a set of calls, executed in pre-defined order).

I’m not saying “do not use it”, but: if you want to share the single instance of that service with all the components that use it — do it, and write the code accordingly.

If you want a new instance of this class whenever some component is instantiated, and have it destroyed when a component is destroyed — do not add `providedIn: root`, just add `Injectable()` and add this service to the `providers` array of that component.

If your class has no dependencies and you don’t need it to be shared across multiple components — then you don’t even need the `Injectable()` decorator, you can just use new.

In my article [Advanced Usage of Angular Dependency Injection](https://medium.com/@eugeniyoz/advanced-usage-of-angular-dependency-injection-399d0a67a938) you can find more examples of using Angular DI in your apps.

---

🪽 Do you like this article? Share it and let it fly! 🛸

💙 *If you enjoy my articles, consider [following me on Twitter](https://twitter.com/eugeniyoz), and/or [subscribing](https://medium.com/@eugeniyoz) to receive my new articles by email.*