【译】解构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)来均可以来获取到这个库。

相关文章
相关标签/搜索