2017-08-03 104 views
0

我一直在進行一個關於在Angular組件中處理狀態的小實驗。Debounce Angular ngDoCheck生命週期鉤子

比方說,我有一個變量的成本高昂,取決於其他幾個。我可以寫一個函數updateVar(),並在我知道可能會影響它的每個事件上調用它,例如。通過訂閱。但是當上述功能在超過5個不同的地方被調用時,它不會感覺非常乾燥或不健壯。

因此,如何去掉DoCheck Angular生命週期鉤子?

docheckSubject: Subject<any> = new Subject(); 
debounced: boolean = false; 

constructor(private cd: ChangeDetectorRef) {} 

ngOnInit() { 
    this.docheckSubject.debounceTime(50).subscribe(
    () => this.ngDoCheckDebounced() 
); 
} 

ngDoCheck() { 
    if (!this.debounced) { 
    this.docheckSubject.next(); 
    } else { 
    this.debounced = false; 
    } 
} 

ngDoCheckDebounced() { 
    this.debounced = true; 
    this.updateVar(); 
    this.cd.detectChanges(); // needed to reflect update in DOM 
} 

這是plunker example

這種方法似乎在我的真實應用程序中正常工作,當然,更多的ngDoCheck()正在發生,而量角器測試也不會抱怨。但我無法擺脫做一個骯髒的不那麼聰明的黑客的感覺。

問題:這會咬我嗎?事實上,我是否需要它?

+0

'每個部件進行檢查時ngDoCheck'叫,什麼是去抖動它的意義呢?也許弄清楚爲什麼這麼頻繁地進行支票? –

+0

它經常被調用,因爲組件具有包含表單的不同數量的子組件(假設平均值爲20)。在用戶選擇時,所有的子組件形式可能會被修補爲新的值。據我所知,這可能會導致~50 ngDoCheck()連續調用。 –

+0

順便說一句我讀過你的其他帖子,例如。 https://stackoverflow.com/questions/42643389;) –

回答

1

ngDoCheck極其通常被稱爲:

https://angular.io/guide/lifecycle-hooks#docheck

This hook is called with enormous frequency—after every change detection cycle no matter where the change occurred.

這是一個壞主意,用這個鉤子比其他變量的任何角度不知道該怎樣變化檢測。 (例如,一個輸入對象改變它的一個屬性的值)。 Debouncing在某種程度上有所幫助,但是您仍然經常執行該邏輯,並且在您不再需要它(即debounced == false)時會繼續執行該操作。

我認爲你的解決方案可行,但我認爲這種方法的開銷比替代方案要差得多。可能需要將更新邏輯作爲Observable鏈的一部分,或將Observables傳遞到將此邏輯添加到鏈中的函數中。

+0

如果您嘗試撬塊,您會發現剔除始終處於打開狀態。'debounced'變量只是防止無限循環的警衛。它應該始終是'假',除非更改是由我的去抖功能觸發的。至於perf,在我的'真實'的例子中,ngDoCheck()可以被稱爲去抖函數的50倍以上,這是一個很大的解脫。 –

+0

關於Observable鏈,這正是我的觀點,在這種情況下,我必須將更新放在相當多的鏈中,因爲它可能來自多個不相關的用戶操作。我想知道替代品。 –

+0

我只是說代碼會繼續執行,對於不清楚的代碼感到抱歉。其中一個潛在的缺點是,如果用戶不知不覺地觸發DoChecks,那麼去抖動將不會解決,直到他們停止並且他們不會看到更新的'var'。 – chrispy

0

你可以把變量放在一個getter中,只有當它被檢索時才重新計算?

下面是一個簡單的例子:

//our root app component 
import {Component, NgModule, VERSION} from '@angular/core' 
import {BrowserModule} from '@angular/platform-browser' 

@Component({ 
    selector: 'my-app', 
    template: ` 
    <div> 
     <h2>Hello {{name}}</h2> 
     <div>x={{x}}</div> 
     <div>y={{y}}</div> 
     <button (click)="increment()">Increment</button> 
     <div>Calculated Value={{calculatedValue}}</div> 
    </div> 
    `, 
}) 
export class App { 
    name:string; 
    x: number = 0; 
    y: number = 10; 

    get calculatedValue(): number { 
    return this.x * this.y; 
    } 

    constructor() { 
    this.name = `Angular! v${VERSION.full}` 
    } 

    increment(): number { 
    this.x++; 
    } 

} 

和相關Plunker:https://plnkr.co/edit/QC7mkbsbjRRBjJOjWSHe?p=preview

+0

我不認爲我可以,因爲我正在更新的狀態通過插值('{{var}}')綁定到DOM,並傳遞到子組件('[var] =「var」 ')。事實上,這正是爲什麼我覺得需要使用有狀態變量的原因,因爲AFAICT綁定到一個沉重的方法等於在ngDoCheck()中調用它,並將其在模板中的出現次數乘以它。 –