【问题标题】:Rxjs Behavior Subject value changes without calling nextRxjs Behavior Subject 值改变而不调用 next
【发布时间】:2020-04-08 11:37:09
【问题描述】:

假设我的服务中有一个行为主体:

theRuleSbj = new BehaviorSubject<Rule>(null);

然后在我的组件中,我使用 getValue() 方法获取该值:

this.rule = this.ruleService.theRuleSbj.getValue();

然后,如果我更改本地规则对象中的一些数据。例如:

this.rule.name = 'name';

我的行为主题值也改变了,没有调用下一个!看来我的变量已绑定到行为主体,因此我的局部变量的每一次更改都会影响我的行为主体。 这是主体行为行为吗!!???还是我做错了什么? (假设我不想改变我的局部变量时的行为主体)。

更新:

我在标记的答案中测试了解决方案,它适用于一个简单的对象。但实际上在我的情况下,如果你有一个嵌套对象(对象内的对象),这是行不通的,我发现this link 中的“深拷贝”术语对我来说非常适用。

【问题讨论】:

    标签: angular rxjs behaviorsubject


    【解决方案1】:

    要让您的价值从Subject 中推出,请从它和subscribe 创建一个Observable

    如果你想要一个本地版本的值,由于对象在 JavaScript 中是通过引用传递的,你需要获取一个副本,所以在订阅时创建一个新对象。

    您可以使用Spread Syntax 来做到这一点。

    然后您可以为本地对象分配您喜欢的任何值,而不会影响Subject

    例如(StackBlitz)

        const theRuleSbj = new BehaviorSubject<Rule>(null);
        const theRule$ = theRuleSbj.asObservable(); 
        
        // The observable will emit null on the initial subscription
        // Subject might be a better fit
        theRule$.subscribe(rule => {
            console.log(`Subscription value:`, rule);
            // Use object spread to create a new object for your component
            this.rule = { ...rule };
          });
        
        // Update the Subject, it will console log new value
        // and update your local value
        theRuleSbj.next({ name: 'Name 1'});
        
        // Update your local value, does not effect your Subject value
        this.rule.name = 'Name 2'; 
        
        // Confirm that they are differant
        console.log(`Subject Value:`, theRuleSbj.getValue());
        console.log(`Local Value`, this.rule);
        
        // Be careful as when you update the Subject, your local value will be updated
        theRuleSbj.next({ name: 'Name 3'});
        console.log(`Subject Value (after subject update):`, theRuleSbj.getValue());
        console.log(`Local Value (after subject update)`, this.rule);
    

    请注意,订阅后,您会将主题值的所有更新推送到您的本地值,您可能希望也可能不希望这种情况发生。

    如果您只想要组件中的一个值,您可以pipe() observable 并使用take(1) 来获取一个值,但是当您将Subject 初始化为BehaviourSubject 时,您只会获取null 值。您可能希望将其更改为 Subject,以便在将第一个值推送到 Subject 时,您的组件会收到它。

    
        const theRuleSbj = new Subject<Rule>();
    
        /* other code omitted  */
    
        theRule$
            .pipe(take(1))
            .subscribe(rule => {
                console.log(`Subscription value:`, rule);
                this.rule = { ...rule };
            });
        
    

    【讨论】:

    • 感谢您的回复和努力。您的解决方案有效,但正如我在更新中所述,我的行为主题中有一个嵌套对象,因此传播语法对我不起作用。我找到了适合我的“深拷贝”解决方案here
    【解决方案2】:

    对象属性在 Javascript 中通过引用传递。有关详细信息,请参阅here。也就是说,使用 getValue() 访问 observable 并不是那么优雅。您可以在不修改源的情况下订阅获取值。

    服务

    theRuleSbj = new BehaviorSubject<Rule>(null);
    ruleObs = this.theRuleSbj.asObservable();
    

    组件

    this.ruleService.ruleObs.subscribe(rule => { this.rule = rule });
    

    【讨论】:

    • 感谢您的回复。您的回答确实有助于我了解发生了什么。特别是您在答案中提供的链接。现在,我知道问题出在哪里,但您的解决方案对我不起作用。
    • 我的意思是,即使我用我的行为主题声明了一个新的 observable,问题仍然存在。正如您答案中的链接所指出的那样,如果我通过以下属性更改对象:this.rule.name = 'name',主题将会改变,但如果我更改整个对象:this.rule = { name = 'name' },行为主体不会改变。
    【解决方案3】:

    这是因为 BehaviorSubject 返回一个指向它当前值的链接。当你做类似的事情时

    this.rule.name = 'name';
    

    对象属性值发生变化,但为所有人共享的链接仍然相同。 如果您需要在本地更改规则值,请尝试

    this.rule = {...this.rule, {name: name}}
    

    它会覆盖您的本地链接而不更改源值

    【讨论】:

    • 感谢您的回复。它对我有用,@Micheal D 的回答让我明白到底发生了什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 2019-12-19
    • 2016-08-10
    • 1970-01-01
    • 2019-11-04
    • 2019-07-08
    相关资源
    最近更新 更多