Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rxjs: Subscriber does not fire on all changes in different class

I have a js class that has a BehaviorSubject and an Observable. I want to subsribe to the Observable in a different class, which does not work, properly.

//class One: 
export default ClassOne {
     constructor(){
         this._store = new BehaviorSubject({}); 
         this.store = this._store.asObservable(); 
         this.store.subscribe(data => {console.log(data)}) //logs the data, no Problem here
         //{};{ test: { foo: "bar", max: "muster" } };{ test: { foo: "bar", max: "muster" } };...
     }
     addData(data){ this._store.next(data)} //iscalled a few times.

     getStore () {return this.store}        //using a getter Function does not work either

}

//class Two
import class1 from "./class1"; 
ClassTwo {
    constructor(){
        this.one = new ClassOne(); 
        this.one.store.subscribe(data =>{console.log(data)}) //only logs {} once. Is never fired again. 
        this.one.getStore().subscribe(data =>{console.log(data)}) //Same Problem
    }
}

So My main questions: How can I make sure that the subscriber gets all the changes in ClassTwo?

Note that the Observable is defined and fires once, but is not notified with new changes.

Does it make a difference when ClassOne is a Singleton?:

  //class One: 
instance = null; 
export default ClassOne {
     constructor(){
         if (instance === null) {
             instance = this;
         }
         instance._store = new BehaviorSubject({}); 
         instance.store = this._store.asObservable(); 
         instance.store.subscribe(data => {console.log(data)}) //logs the data, no Problem here
     }
     addData(data){ instance._store.next(data)} //iscalled a few times. 
     getStore () {return instance.store}        //using a getter Function does not work either
}

Edit: Test if its a singleton (using jest)

beforeAll(() => {
   one = new ClassOne();
});
test("Singleton Test", () => {
  let one2 = new ClassOne();
  expect(one2 instanceof ClassOne).toEqual(true);
  expect(one2).toEqual(one);
  expect(one2 === one).toEqual(true);
});

Edit2: Use of Add data

beforeAll(() => {

one = new ClassOne();
two = new ClassTwo(); 
});
  test("", (done) => {
    one.addData({ test: { foo: "bar", max: "muster" } });
    one.addData({ test: { foo: "bar", max: "muster" } });
    one.addData({ test: { foo: "bar", max: "muster" } });        

    //I just coded this by heart, but the test is somthing like this
    expect(one.getStore()).toEqual(two.one.store);

    //makes sure that the subscriber have enough time
    setTimeout(() => done(), 5000);
  }, 6000);

Edit3: Using share/shareReplay

I have changed the getStore() function:

//ClassOne
getStore(){ return instance.store.pipe(share()) }
//and I tried: 
getStore(){ return instance.store.pipe(shareReplay()) }

//ClassTwo: 
this.one.getStore().subscribe(data =>{console.log(data)}) 

But the Problem remains.

Edit4: Some Trouble shooting

So after some testing and reading I come to the conclusion that subscriptions in constructors are not working properly.

While this is not working:

   let two = new ClassTwo(); //with the subscription on the store

This works:

   let two = new ClassTwo(); //without the subscription on the store
   two.subscribeToMyStore(); 

   //subscribeToMyStore: 
   subscribeToMyStore(){
       this.one.store.subscribe(data =>{console.log(data)})
   }

So why cant I do subscriptions in the constructor?

And why would it subscribe in the constructor of ClassOne but not ClassTwo?

like image 534
telion Avatar asked May 19 '20 09:05

telion


1 Answers

You have to reuse the same instance. Either ensure it is singleton and inject in through the constructor using some DI framework, or export singleton yourself from separate moudle and don't forget to share it. Note that there exist quite popular scenarios when shareReplay is needed.

like image 60
Zazeil Avatar answered Sep 28 '22 11:09

Zazeil