Search code examples
angularrxjsbehaviorsubject

BehaviorSubject: why is it working without using next


I have an Angular app and I created a class for my contact list which consists of:

export interface Contact {
    id: number;
    name: string;
    address: string;
}

export class ContactList {
    private contactList: Contact[];
    private contactNumber: number;

public getContactList(): Contact[] {
        return this.contactList;
    }

// Methods to add, modify and remove a contact
}

Then I have a service that instantiates this class, creates a BehaviorSubject to share it with other components and has some public methods.

export class ContactListService {

  public contactList: ContactList;
  private contactList$: BehaviorSubject<Contact[]>;

  constructor() {
    this.contactList = new ContactList(FAKE_CONTACTS);
    this.contactList$ = new BehaviorSubject<Contact[]>(this.contactList.getContactList());
  }

  public getContactList(): BehaviorSubject<Contact[]> {
    return this.contactList$;
  }

  public deleteContact(contactId: number): void {
    this.contactList.deleteContact(contactId);
  }

  public addContact(newName: string, newAddress: string): void {
    this.contactList.addContact(newName, newAddress);
  }

  public modifyContact(contactId: number, newName?: string, newAddress?: string): void {
    this.contactList.modifyContact(contactId, newName, newAddress);
  }
}

Then, in a component, I subscribe to the BehaviorSubject and affect the value to a property of my component.

ngOnInit() {
    this.contactListSubscription = this.contactListService.getContactList().subscribe((newContactList) => {
      this.contactList = newContactList;
    });
  }

So it's working (ie. everything is update everywhere when I perform an action via the service). But what I don't understand is that the content of the subscription (ie. this.contactList = newContactList) is only performed one time at the subscription and not every time an action occurs. Even if I change the content via a contactListService method. And even if I unsubscribe, like 2s after subscribing (with a setTimeout for example), the content is always up to date after the unsubscribing...

At first, I even didn't understood why it works in the services without doing a contactList$.next(this.contactList.getContactList()) after every actions that modify the object.

So it seems like I passed some references instead of the content of the class? I think I don't understand how BehaviorSubject works!


Solution

  • JavaScript always passes references to objects.

    So the contactList array in the component is a reference to the same, unique array stored in your ContactList.contactList property.

    So, you modify the array, the component has a reference to this modified array, and Angular detects the changes in the array, and applies the changes to the DOM.

    It wouldn't work anymore if the methods in ContactList replaced the array by another one (a modified copy), i.e. did, for example

    this.contactList = [this.contactList..., newContact]
    

    In that case, the component would keep referencing the previous, unmodified array.

    To illustrate with code:

    service.contactList = ['John', 'Mary'];
    component.contactList = service.contactList; // the exact, same array
    
    service.contactList.push('Helen'); // add an element to the unique array
    console.log(component.contactList); // logs John, Mary, Helen: it's the same, unique array
    
    service.contactList = ['Jack']; // now the service references a second, different array
    console.log(component.contactList); // Still logs John, Mary, Helen