DEV Community

Cover image for Globally Shared Instances in Angular — Use with Care
Evgeniy OZ
Evgeniy OZ

Posted on • Originally published at Medium

Globally Shared Instances in Angular — Use with Care

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:

    import {Injectable} from '@angular/core';
    @Injectable({
      providedIn: 'root',
    })
    export class CalculatorService {
      scale = 1;

      add(x: number, y: number) {
        return (x + y) * this.scale;
      }
    }
Enter fullscreen mode Exit fullscreen mode

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 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, and/or subscribing to receive my new articles by email.

Top comments (0)