React Reflux

概念

Reflux是根據React的flux建立的單向數據流類庫。
Reflux的單向數據流模式主要由actions和stores組成。例如,當組件list新增item時,會調用actions的某個方法(如addItem(data)),並將新的數據當參數傳遞進去,經過事件機制,數據會傳遞到stroes中,stores能夠向服務器發起請求,並更新數據數據庫。數據更新成功後,仍是經過事件機制傳遞的組件list當中,並更新ui。整個過程的對接是經過事件驅動的。就像這樣:react

╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └──────────────────────────────────────┘

代碼看起來像這樣的:git

var TodoActions = Reflux.createActions([
    'addItem'
]);

var TodoStore = Reflux.createStore({
    items: [1, 2],
    listenables: [TodoActions],
    onAddItem: function (model) {
        $.post('/server/add', {data: model}, function (data) {
            this.items.unshift(data);
            this.trigger(this.items);
        });
    }
});


var TodoComponent = React.createClass({
    mixins: [Reflux.listenTo(TodoStore, 'onStatusChange')],
    getInitialState: function () {
        return {list: []};
    },
    onStatusChange: function () {
        this.setState({list: TodoStore.items});
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});


React.render(<TodoComponent />, document.getElementById('container'));

同React Flux比較

相同點

  • 有actions
  • 有stores
  • 單向數據流

不一樣點

  • 經過內部拓展actions的行爲,移除了單例的dispatcher
  • stores能夠監聽actions的行爲,無需進行冗雜的switch判斷
  • stores能夠相互監聽,能夠進行進一步的數據聚合操做,相似於,map/reduce
  • waitFor被連續和平行的數據流所替代

建立Action

var statusUpdate = Reflux.createAction(options);

返回值是一個函數,調用這個函數就會觸發相應的事件,在store中監聽這個函數,並做相應的處理github

var addItem = Reflux.createAction();

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (model) {
        console.log(model);
    }
});

addItem({name: 'xxx'});

建立多個Action

var TodoActions = Reflux.createActions([
    'addItem',
    'deleteItem'
]);

store監聽actions的行爲:數據庫

var TodoActions = Reflux.createActions([
    'addItem',
    'deleteItem'
]);

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(TodoActions.addItem, 'addItem');
        this.listenTo(TodoActions.deleteItem, 'deleteItem');
    },
    addItem: function (model) {
       console.log(model)
    },
    deleteItem:function(model){
        console.log(model);
    }
});

TodoActions.addItem({name:'xxx'});
TodoActions.deleteItem({name:'yyy'});

異步Action

真實的應用場景中,幾乎全部的操做都會向後端請求,而這些操做都是異步的,Reflux也提供了相應的Promise接口後端

var getAll = Reflux.createAction({asyncResult:true});

例如獲取所有數據:promise

var getAll = Reflux.createAction({asyncResult: true});

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(getAll, 'getAll');
    },
    getAll: function (model) {
        $.get('/all', function (data) {
            if (data) {
                getAll.completed(data);
            } else {
                getAll.failed(data);
            }

        });
    }
});

getAll({name: 'xxx'})
    .then(function (data) {
        console.log(data);
    })
    .catch(function (err) {
        throw err;
    });

Action hooks

Reflux爲每一個action都提供了兩個hook方法服務器

  • preEmit(params),action emit以前調用,參數是action傳遞過來的,返回值會傳遞給shouldEmit
  • shouldEmit(params) action emit以前調用,參數默認是action傳遞,若是preEmit有返回值,則是preEmit返回值,返回值決定是否emit

情景一:異步

var addItem = Reflux.createAction({
    preEmit: function (params) {
        console.log('preEmit:' + params);           
    },
    shouldEmit: function (params) {
        console.log('shouldEmit:' + params);           
    }
});

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (params) {
        console.log('addItem:' + params);
    }
});

addItem('xxx');

控制檯打印
$ preEmit:xxx
$ shouldEmit:xxx

情景二:async

var addItem = Reflux.createAction({
    preEmit: function (params) {
        console.log('preEmit:' + params);
        return 324;
    },
    shouldEmit: function (params) {
        console.log('shouldEmit:' + params);
        return true;
    }
});

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (params) {
        console.log('addItem:' + params);
    }
});

addItem('xxx');

控制檯打印
$ preEmit:xxx
$ shouldEmit:324
$ addItem:324

注意幾個返回值和參數的關係函數

Action Methods

當須要給全部的action添加公用方法時,能夠這麼幹:

Reflux.ActionMethods.print = function (str) {
    console.log(str);
};

var addItem = Reflux.createAction();

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (params) {
        console.log('addItem:' + params);
    }
});

addItem.print('xxx');

trigger、triggerAsync和triggerPromise

直接調用addItem()其實是調用trigger或者triggerAsync或者triggerPromise,它們區別在於

var addItem = Reflux.createAction(); addItem();                 #默認調用triggerAsync,至關於addItem.triggerAsync()
var addItem = Reflux.createAction({sync:true});addItem();       #默認調用trigger,至關於addItem.trigger()
var addItem = Reflux.createAction({asyncResult:true});addItem();#默認調用triggerPromise,至關於addItem.triggerPromise()

trigger和triggerAsync區別在於:

triggerAsync = setTimeout(function () {
    trigger()
}, 0);

trigger和triggerPromise區別在於,triggerPromise的返回值是promise

建立Store

Store能夠響應Action的行爲,並同服務器交互。

監聽單個Action

在init方法中添加監聽處理

var addItem = Reflux.createAction();

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (model) {
        console.log(model);
    }
});

addItem({name: 'xxx'});

監聽多個Action

做死寫法

var TodoActions = Reflux.createActions([
    'addItem',
    'deleteItem'
]);

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(TodoActions.addItem, 'addItem');
        this.listenTo(TodoActions.deleteItem, 'deleteItem');
    },
    addItem: function (model) {
        console.log(model);
    },
    deleteItem: function (model) {
        console.log(model);
    }
});

TodoActions.addItem({name: 'xxx'});
TodoActions.deleteItem({name: 'yyy'});

兩個action的時候在init裏寫了兩遍監聽處理方法,若是有十個甚至多個的話,寫起來就像這樣的:

var TodoActions = Reflux.createActions([
    'item1',
    'item2',
    'item3',
    'item4',
    'item5',
    'item6',
    'item7',
    'item8',
    'item9',
    'item10'
]);

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(TodoActions.item1, 'item1');
        this.listenTo(TodoActions.item2, 'item2');
        this.listenTo(TodoActions.item3, 'item3');
        this.listenTo(TodoActions.item4, 'item4');
        this.listenTo(TodoActions.item5, 'item5');
        this.listenTo(TodoActions.item6, 'item6');
        this.listenTo(TodoActions.item7, 'item7');
        this.listenTo(TodoActions.item8, 'item8');
        this.listenTo(TodoActions.item9, 'item9');
        this.listenTo(TodoActions.item10, 'item10');

    },
    item1: function (model) {
        console.log(model);
    },
    item2: function (model) {
        console.log(model);
    }
});

TodoActions.item1({name: 'xxx'});
TodoActions.item2({name: 'yyy'});

listenToMany

還好Reflux給咱們提供了listenToMany方法,避免重複勞動:

var TodoActions = Reflux.createActions([
    'item1',
    'item2',
    'item3',
    'item4',
    'item5',
    'item6',
    'item7',
    'item8',
    'item9',
    'item10'
]);

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenToMany(TodoActions);
    },
    onItem1: function (model) {
        console.log(model);
    },
    onItem2: function (model) {
        console.log(model);
    }
});

TodoActions.item1({name: 'xxx'});
TodoActions.item2({name: 'yyy'});

處理方法只需讓action的標識首字母大寫並加上on就能夠了。

標識若是首字母大寫就會識別不了,例如將上面的item1改爲Itme1。這坑爹的!

listenables

var TodoActions = Reflux.createActions([
    'item1',
    'item2',
    'item3',
    'item4',
    'item5',
    'item6',
    'item7',
    'item8',
    'item9',
    'item10'
]);

var TodoStore = Reflux.createStore({
    listenables: [TodoActions],
    onItem1: function (model) {
        console.log(model);
    },
    onItem2: function (model) {
        console.log(model);
    }
});

TodoActions.item1({name: 'xxx'});
TodoActions.item2({name: 'yyy'});

通常咱們寫真實應用的時候都應該採用這種寫法!!!

Store Methods

拓展Store的公用方法有兩種方式。

方式一

Reflux.StoreMethods.print = function (str) {
    console.log(str);
};

var addItem = Reflux.createAction();

var TodoStore = Reflux.createStore({
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (model) {
        console.log(model);
    }
});

TodoStore.print('rrr');

方式二

var Mixins = {
    print: function (str) {
        console.log(str);
    }
}

var addItem = Reflux.createAction();

var TodoStore = Reflux.createStore({
    mixins: [Mixins],
    init: function () {
        this.listenTo(addItem, 'addItem');
    },
    addItem: function (model) {
        console.log(model);
    }
});

TodoStore.print('rrr');

同組件結合

前面說了,Action、Store和組件這三者是經過事件機制響應變化的,構建組件的時候首先須要監聽Store的狀態。
先定義Action和Store

var TodoActions = Reflux.createActions([
    'getAll'
]);

var TodoStore = Reflux.createStore({
    items: [1,2,3],
    listenables: [TodoActions],
    onGetAll: function () {
        this.trigger(this.items);
    }
});

基本

var TodoComponent = React.createClass({
    getInitialState: function () {
        return {list: []};
    },
    onStatusChange: function (list) {
        this.setState({list: list});
    },
    componentDidMount: function () {
        this.unsubscribe = TodoStore.listen(this.onStatusChange);
        TodoActions.getAll();
    },
    componentWillUnmount: function () {
        this.unsubscribe();
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});    
React.render(<TodoComponent />, document.getElementById('container'));

這裏有兩點須要注意:

  • 當組件的生命週期結束時須要解除對Store的監聽
  • 當Store調用trigger時,纔會執行onStatusChange函數,因此每次Store更新時,須要手動調用trigger函數

Mixins

var TodoComponent = React.createClass({
    mixins: [Reflux.ListenerMixin],
    getInitialState: function () {
        return {list: []};
    },
    onStatusChange: function (list) {
        this.setState({list: list});
    },
    componentDidMount: function () {
        this.unsubscribe = TodoStore.listen(this.onStatusChange);
        TodoActions.getAll();
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});
React.render(<TodoComponent />, document.getElementById('container'));

Reflux.listenTo

var TodoComponent = React.createClass({
    mixins: [Reflux.listenTo(TodoStore,'onStatusChange')],
    getInitialState: function () {
        return {list: []};
    },
    onStatusChange: function (list) {
        this.setState({list: list});
    },
    componentDidMount: function () {
        TodoActions.getAll();
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});
React.render(<TodoComponent />, document.getElementById('container'));

Reflux.connect

var TodoComponent = React.createClass({
    mixins: [Reflux.connect(TodoStore,'list')],
    getInitialState: function () {
        return {list: []};
    },
    componentDidMount: function () {
        TodoActions.getAll();
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});
React.render(<TodoComponent />, document.getElementById('container'));

數據會自動更新到state的list當中。

Reflux.connectFilter

var TodoComponent = React.createClass({
    mixins: [Reflux.connectFilter(TodoStore, 'list', function (list) {
        return list.filter(function (item) {
            return item > 1;
        });
    })],
    getInitialState: function () {
        return {list: []};
    },
    componentDidMount: function () {
        TodoActions.getAll();
    },
    render: function () {
        return (
            <div>
                {this.state.list.map(function (item) {
                    return <p>{item}</p>
                })}
            </div>
        )
    }
});
React.render(<TodoComponent />, document.getElementById('container'));

對數據加了一層過濾器。

以上便Component同Store交互的內容,你們能夠根據實際狀況選擇不一樣的寫法。

小結

我這人喜歡拿代碼來表述思想。

var TodoActions = Reflux.createActions([
    'getAll',
    'addItem',
    'deleteItem',
    'updateItem'
]);

var TodoStore = Reflux.createStore({
    items: [1, 2, 3],
    listenables: [TodoActions],
    onGetAll: function () {
        $.get('/all', function (data) {
            this.items = data;
            this.trigger(this.items);
        }.bind(this));
    },
    onAddItem: function (model) {
        $.post('/add', model, function (data) {
            this.items.unshift(data);
            this.trigger(this.items);
        }.bind(this));
    },
    onDeleteItem: function (model, index) {
        $.post('/delete', model, function (data) {
            this.items.splice(index, 1);
            this.trigger(this.items);
        }.bind(this));
    },
    onUpdateItem: function (model, index) {
        $.post('/update', model, function (data) {
            this.items[index] = data;
            this.trigger(this.items);
        }.bind(this));
    }
});


var TodoComponent = React.createClass({
    mixins: [Reflux.connect(TodoStore, 'list')],
    getInitialState: function () {
        return {list: []};
    },
    componentDidMount: function () {
        TodoActions.getAll();
    },   
    render: function () {
        return (
            <div>
                {this.state.list.map(function(item){
                    return <TodoItem data={item}/>
                })}
            </div>
        )
    }
});

var TodoItem = React.createClass({
    componentDidMount: function () {
        TodoActions.getAll();
    },
    handleAdd: function (model) {
        TodoActions.addItem(model);
    },
    handleDelete: function (model,index) {
        TodoActions.deleteItem(model,index);
    },
    handleUpdate: function (model) {
        TodoActions.updateItem(model);
    },
    render: function () {
        var item=this.props.data;
        return (
            <div>
                <p>{item.name}</p>
                <p>{item.email}</p>
                <p>/*操做按鈕*/</p>
            </div>
        )
    }
});
React.render(<TodoComponent />, document.getElementById('container'));

實際狀況遠比這複雜,只是提供一個思路供你們參考。

代碼連接

github

參考

Reflux

相關文章
相關標籤/搜索