【譯】解構ReactJS的Flux

用ReactJS時不要使用MVC

我將經過列出一些單向數據流的例子來將ReactJS官方實現的Flux和我寫的庫Reflux做比較。git

Facebook的ReactJS開發小組彷佛並不待見MVC框架。將MVC模式和ReactJS結合使用了一段時間後,我彷佛發現了爭議從何而來了。你會遇到一個問題:你應該如何處理數據?ReactJS並不在意太多關於數據是如何傳入的或者貫穿整個Web應用去處理數據。這個幾乎是一個架構層面的問題,並非ReactJS所能涵蓋的。因而Facebook中的優秀開發者提出了一個函數式的方法,他們稱其爲:Fluxgithub

Flux的基本思想是能夠在Web應用中擁有一個更加函數式的方法來處理數據。Flux介紹了Actions和Data Stores的概念來處理整個應用的事件和數據。數據流大體是這個樣子的:npm

Action -> Data Store -> Component

數據的突變必須是在調用Actions時發生的,Data Stores須要監聽actions而且改變store中的數據。這讓數據結構保持扁平,並讓數據的改變操做始終發生在Stores中,這防止了讓Components本身處理數據所帶來的反作用。編程

經過使用單向數據流,跟蹤數據的改變將更加容易,由於它徹底依賴於actions是如何發佈的,繼而影響整個應用。Components自身僅經過執行調用action來改變應用數據,這樣避免了維護上的麻煩。數據結構

Todo Example vs Reflux

這裏有一個供參考的例子,是官方的Todo-mvc。我將基於這個例子來作個人解構。架構

Facebook喜歡將Flux說成是經過函數式編程來建立應用的一種方式,不過我發現了一些過去命令式編程的趨勢而且能夠它能夠被改進得更加簡單而且更加具備函數式的實現。mvc

Dispatcher的古怪

TODO的實現涉及到了一個Dispatcher,它打包了全部的actions。接下來,Data Stores須要這樣:框架

  • 註冊本身去監聽action的事件,全部的actions
  • 爲了區分actions之間的區別,Stores須要比較它們要監聽的action的名字(靜態字符串)

後面那一點讓我感受困惑,由於它有一點破壞了JavaScript能夠作到的函數式編程之美。我很抵觸去比較類型,不管是經過字符串仍是用instanceof,由於它摒棄了多態性,彷彿是在維護一堆蠕蟲。dom

Reflux中,我決定將Dispatcher合併進Actions中,去掉了其單例的實現。因此當你在使用actions的時候,你的應用僅須要作兩作事:函數式編程

  • 建立actions
  • 經過回調函數來監聽action的調用

Actions是經過Reflux.createAction來建立的,傳遞一個回調函數給action的listen方法,因而它就能夠被任何Data Store監聽。

// Creating action
var toggleGem = Reflux.createAction();

// Listening to action
var isGemActivated = false;

toggleGem.listen(function() { 
  isGemActivated = !isGemActivated;
  var strActivated = isGemActivated ?
    'activated' :
    'deactivated';
  console.log('Gem is ' + strActivated);
});

Reflux中的Actions是帶有event emitters的函數。若是你想了解它的一個簡單的實現,能夠參考這裏

在你的應用中修改數據只須要調用action:

toggleGem();

// The callback that listens to the action will output
// "Gem is activated"

toggleGem();

// Will output "Gem is deactivated"

讓actions成爲一個函數,而不是維護一堆靜態字符串:

var Actions = {};

Actions.toggleGem = createAction(); 
Actions.polishGem = createAction(); 
// and so on...

Data Stores也能夠這樣相似的實現,Reflux提供了一個方便的createStore函數。那些涉足ReactJS組件開發的開發者會把它認爲是和React.createClass同樣能夠工做:

// Creates a DataStore
var gemStore = Reflux.createStore({

    // Initial setup
    init: function() {
        this.isGemActivated = false;

        // Register statusUpdate action
        this.listenTo(toggleGem, this.handleToggleGem);
    },

    // Callback
    handleToggleGem: function() {
        this.isGemActivated = !this.isGemActivated;

        // Pass on to listeners through
        // the DataStore.trigger function
        this.trigger(this.isGemActivated);
    }

});

在一個store實例上有兩個方便的方法:listenTo用於action的註冊,trigger用於觸發DataStore的change事件。

ReactJS組件能夠經過監聽這些Data Stores的方式來使用它們:

var Gem = React.createClass({ 
    componentDidMount: function() {
        // the listen function returns a
        // unsubscription convenience functor
        this.unsubscribe =
            gemStore.listen(this.onGemChange);
    },

    componentWillUnmount: function() {
        this.unsubscribe();
    },

    // The listening callback
    onGemChange: function(gemStatus) {
        this.setState({gemStatus: gemStatus});
    },

    render: function() {
        var gemStatusStr = this.state.gemStatus ?
            "activated" :
            "deactivated";   
        return (React.DOM.h1(null,
            'Gem is ' + gemStatusStr));
    }
});

waitFor到底在等什麼?

另外一個讓我困惑的是TodoList例子的代碼中包含了一個waitFor,函數式的特性被公然破壞了。狀況是這樣的:一個Data Store須要等待其餘Data Store在特定的action被執行後完成它們的數據處理,這彷佛違背了單向數據流的原則。

不如讓Data Store一樣是能夠被監聽的。在Reflux中,你可讓一個Data Store直接監聽另外一個Data Store的change事件來處理上述狀況。

結論

在你的ReactJS項目中試試用Reflux,它使得設置Flux架構更加簡單。bower(bower install reflux)或者npm(npm install reflux)來均可以來獲取到這個庫。

相關文章
相關標籤/搜索