React Reflux

来源:互联网 发布:美国大学网络教育 编辑:程序博客网 时间:2024/06/05 05:07

概念

Reflux是根据React的flux创建的单向数据流类库。
Reflux的单向数据流模式主要由actions和stores组成。例如,当组件list新增item时,会调用actions的某个方法(如addItem(data)),并将新的数据当参数传递进去,通过事件机制,数据会传递到stroes中,stores可以向服务器发起请求,并更新数据数据库。数据更新成功后,还是通过事件机制传递的组件list当中,并更新ui。整个过程的对接是通过事件驱动的。就像这样:

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

代码看起来像这样的:

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中监听这个函数,并作相应的处理

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});

例如获取全部数据:

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

情景二:

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'));

实际情况远比这复杂,只是提供一个思路供大家参考。

长按图片识别图中二维码(或搜索微信公众号FrontEndStory)关注“前端那些事儿”,带你探索前端的奥秘。


0 0
原创粉丝点击