简体   繁体   中英

BehaviorSubject partial change does not trigger subscription

I'm working with Typescript 3.4.5 and Angular 8.

Consider the following interface:

// This is an interface which represents a piece of data, which will be partially updated
export interface TextBrick {
  kind: 'text';
  content: string;
}

export class TestService {
  test$ = new BehaviorSubject<TextBrick>({kind: 'text', content: 'initial'});

  get test(): TextBrick {
    return this.test$.value;
  }

  set test(v: TextBrick) {
    console.log('set', v);
    this.test$.next(v);
  }
}

The idea is to subscribe to the test$ BehaviorSubject to watch the test.content change.

Consider now the following test:

test('test', () => {
  const instance = new TestService();

  // Watch subscription
  instance.test$.subscribe((v) => console.log('sub', v));

  // 1) Full replace, subscription triggered
  instance.test = {kind: 'text', content: 'fullreplace'};

  // 2) Partial replace, subscription not triggered
  instance.test.content = 'aa';

  // But the value of the BehaviorSubject was updated! WTF?!
  console.log('end', instance.test); 
});

The console output is the following:

sub { kind: 'text', content: 'intitial' }    // Expected behavior
set { kind: 'text', content: 'fullreplace' } // Expected behavior
sub { kind: 'text', content: 'fullreplace' } // Expected behavior
end { kind: 'text', content: 'aa' }          // Sounds really weird!

There is obviously a problem with "partial setter" (I don't know how to name that), when I set the instance.test.content . I carefully read the Typescript documentation on setters , but this use case is not mentioned.

My first hypothesis was that the set test() was not called, which make sense because when I add a console.log in the setter, I don't see the "partial" set of 'aa' . But how can be the value of the behavior subject updated without triggering my subscription callback?

Any help or ressource will be greatly appreciated!

instance.test = {kind: 'text', content: 'fullreplace'};

This line calls the setter function

instance.test.content = 'aa';

This line calls the getter function and then mutates the contents of the behavior subject, you should not mutate the contents of the behavior subject.

Get the value and then update the value with a new object, we don't mutate objects in the world of reactive programming.

const value = instance.test;

instance.test = { ...value, content: 'aa' };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM