高效的Mobx模式(Part 2 - 掌握資料變更方法)

weixin_33806914發表於2018-09-05

在上一部分中,我們研究瞭如何設定MobX狀態樹並使其可觀察。 有了這個,下一步就是開始對變化作出反應。 坦率地說,這就是有趣的開始!

MobX保證只要您的響應資料圖發生變化,依賴於可觀察屬性的部分就會自動同步。 這意味著您現在可以專注於對變化做出反應並引起的副作用,而不是擔心資料同步。

讓我們深入研究一下可以引起副作用的各種方法。

使用@action作為入口點

預設情況下,當您修改observable時,MobX將檢測並保持其他依賴的可觀察物件同步。 這是同步發生的。 但是,有時您可能希望在同一方法中修改多個observable。 這可能會導致多個通知被觸發,甚至可能會降低您的應用速度。
action

更好的方法是action()中包裝要呼叫的方法。 這會在您的方法周圍建立一個事務邊界,並且所有受影響的observable將在您執行操作後保持同步。 請注意,此延遲通知僅適用於當前函式範圍中的observable。 如果您具有修改更多可觀察物件的非同步操作,則必須將它們包裝在runInAction()中。

class Person {
    @observable firstName;
    @observable lastName;

    // 因為我們在@action中包裝了此方法,所以只有在changeName()成功執行後,fullName才會更改
    @action changeName(first, last) {
        this.firstName = first;
        this.lastName = last;
    }

    @computed get fullName() {
        return `${this.firstName}, ${this.lastName}`;
    }
}

const p = new Person();
p.changeName('Pavan', 'Podila');

Actions是改變Store的切入點。 通過使用Actions,您可以將多個observable更新為原子操作。

儘可能避免直接從外部操縱observable並公開@action方法為你做這個改變。 實際上,可以通過設定useStrict(true)來強制執行此操作。

使用@autorun觸發副作用

MobX確保可觀察圖形始終保持一致。 但如果這個世界只是關於可觀察的東西,那就不好玩了。 我們需要他們的同行:觀察者使事情變得有趣。

實際上,UI是mobx store的美化觀察者。 使用mobx-react,您將獲得一個繫結庫,使您的React元件可以觀察儲存並在儲存更改時自動呈現。

但是,UI不是系統中唯一的觀察者。 您可以向store新增更多觀察者以執行各種有趣的事情。 一個非常基本的觀察者可能是一個控制檯記錄器,它只是在可觀察的變化時將當前值記錄到控制檯。

通過autorun,我們可以非常輕鬆地設定這些觀察者。 最快的方法是提供autorun功能。 MobX會自動跟蹤您在此函式中使用的任何可觀察物件。 每當它們改變時,你的功能都會重新執行(也就是自動執行)!

class Person {

    @observable firstName = 'None';
    @observable lastName = 'None';

    constructor() {

        // A simple console-logger
        autorun(()=>{
            console.log(`Name changed: ${this.firstName}, ${this.lastName}`);
        });

        // 這裡會導致autorun()執行
        this.firstName = 'Mob';

        // autorun()再一次執行
        this.lastName = 'X';
    }
}

// Will log: Name changed: None, None
// Will log: Name changed: Mob, None
// Will log: Name changed: Mob, X

正如您在上面的日誌中所看到的,自動執行將立即執行,並且每次跟蹤的可觀察量發生變化時也會執行。 如果您不想立即執行,而是僅在發生更改時執行,該怎麼辦? 請繼續閱讀。


首次更換後使用reaction觸發副作用

autorun相比,reaction提供了更細粒度的控制。 首先,它們不會立即執行並等待對跟蹤的可觀察量的第一次更改。 API也與autorun略有不同。 在最簡單的版本中,您提供兩個輸入引數:

reaction(()=> data, data => { /* side effect */})

第一個函式(跟蹤函式 tracking function)應該返回將用於跟蹤的資料。 然後將該資料傳遞給第二個函式(效果函式 effect function)。 不跟蹤效果函式,您可以在此處使用其他可觀察物件。

預設情況下,reaction將不會在第一次執行,並將等待追蹤函式的變更。 只有當tracking function返回的資料發生變化時,才會執行副作用。 通過將原始自動執行分解為tracking function +effect function,您可以更好地控制實際導致副作用的內容。

import {reaction} from 'mobx';

class Router {

    @observable page = 'main';

    setupNavigation() {
        reaction(()=>this.page, (page)=>{
            switch(page) {
                case 'main':
                    this.navigateToUrl('/');
                    break;

                case 'profile':
                    this.navigateToUrl('/profile');
                    break;

                case 'admin':
                    this.navigateToUrl('/admin');
                    break;
            }
        });
    }

    navigateToUrl(url) { /* ... */ }
}

在上面的示例中,我在載入“main”頁面時不需要導航。 一個reaction使用的完美案例。 僅當路由器的頁面屬性發生更改時,才會導航到特定URL。

以上是一個非常簡單的路由器,具有固定的頁面集。 您可以通過向URL新增頁面地圖來使其更具可擴充套件性。 使用這種方法,路由(使用URL更改)會成為更改Store某些屬性的副作用。

使用when觸發一次性的副作用

autorunreaction是持續的副作用。 初始化應用程式時,您將建立此類副作用,並期望它們在應用程式的生命週期內執行。

我之前沒有提到的一件事是這兩個函式都返回一個處理器函式。 您可以隨時呼叫該處理器函式並取消副作用。

const disposer = autorun(()=>{ 
    /* side-effects based on tracked observables */ 
});

// .... At a later time
disposer(); // Cancel the autorun 

現在我們構建的應用程式有各種用例。 您可能希望某些副作用僅在您到達應用程式中的某個點時執行。 此外,您可能希望這些副作用只執行一次,然後再也不會執行。

讓我們舉一個具體的例子:比如說,當使用者到達應用程式中的某個里程碑時,您希望向使用者顯示一條訊息。 此里程碑僅對任何使用者發生一次,因此您不希望設定持續執行的副作用,如autorunreaction。 現在是時候拿出when 這個API來完成這項工作了。

當拿出兩個引數時,就像reaction一樣。 第一個(跟蹤器函式)應該返回一個布林值。 當這變為真時,它將執行效果函式,第二個引數為when。 最棒的部分是它會在執行後自動處理副作用。 因此,無需跟蹤處理器並手動呼叫它。

when(()=>this.reachedMilestone, ()=>{
    this.showMessage({ 
        title: 'Congratulations', 
        message: 'You did it!'
    });
})


到目前為止,我們已經看到了各種技術來跟蹤物件圖上的變化,並對這些變化做出反應。 MobX提高了抽象級別,以便我們可以在更高階別進行思考,而不必擔心跟蹤和對變化做出反應的意外複雜性。

我們現在有了一個基礎,可以構建依賴於域模型更改的強大系統。 通過將域模型之外的所有內容視為副作用,我們可以提供視覺反饋(UI)並執行許多其他活動,如監控,分析,日誌記錄等。

相關文章