blogs icon indicating copy to clipboard operation
blogs copied to clipboard

Reflux 使用进化日记

Open yibuyisheng opened this issue 9 years ago • 14 comments

Reflux 算是比较新的东西,由于自己水平有限,刚接触,不能很好地去使用 Reflux 来处理数据,下面是我使用 Reflux 逐步进化的过程(当然最终状态不一定就是标准的 Reflux 使用方式):

第一步:初识 Reflux

一直在听人说 Reflux ,说这个东西比较适合中小型的前端项目,使用起来很方便,于是我就找到了 Reflux 在 GitHub 的主页

文档说 dispatcher 被移除了,没关系,反正我也没用过 Flux 。

于是继续阅读关于 actions 和 stores 的文档。由于心浮气躁急着用,看文档很马虎,action 、 store 可以监听过去监听过来的,还有 store 可以 connect 啥的,完全看晕了,无法用 Reflux 组织起一个完整的处理流程。但是没关系,我就按照文档上的这些 listen 啥的,自己来写写看吧。

于是, 创建 action ,在 store 中用 listenTo 来监听 action ,然后请求数据,store trigger 返回数据。写的时候,由于完全不理解 Reflux 怎么用,一通胡乱监听,写出来的代码不三不四,看着都觉得累。为了照顾项目进度,放弃 Reflux ,自己写一个 service 层吧。

第二步:认识了一点 Reflux

过了几天,对 Reflux 心有不甘,于是转头再去看 Reflux 文档,同时也很开心找到一篇使用 Reflux 的经验文章,于是知道了 action 可以当成方法调用,在 action 中监听调用,发出请求之类的,然后 store 做一些存储等操作,再 trigger ,component 中通过 mixin 来监听 store 中的 trigger ,然后做一些界面变动,摘录一段那篇文章中的例子:

var Reflux = require('reflux');
var React = require('react');

var UserAction = Reflux.createAction({
    'login': {children: ['success', 'failed']}
});

UsersAction.login.listen(function(data) {
    $.post('/api/users/Action/login', data).then(this.success, this.failed);
});

var UserStore = Reflux.createStore({
    listenables: UserAction,
    onLoginSuccess: function(payload) {
        this.trigger(payload);
    },
    onLoginFailed: function(payload) {
        this.trigger(payload);
    }
});

var UserComponent = React.createClass({
    mixins: [Reflux.connect(UserStore, 'user')],
    render: function() {
        return <span>{this.state.user.name}</span>;
    }
});

感觉自己似乎知道怎么来组织流程了,于是很开心地又去改造代码,希望能用上 Reflux 。

写了一会儿,发现完了,因为有这样的场景:就拿上述一小段代码来说,UserAction 中很可能还有其它 action ,例如:

var UserAction = Reflux.createActions({
    'login': {children: ['success', 'failed']},
    'register': {children: ['success', 'failed']}
});

login 和 register 两个 action 都会触发 UserStore 中相应方法的调用,然后这些方法再调用 trigger ,然后改变 UserComponent 中 state.user 的值,此处有两个问题:

  • 1、登录和注册最终得到的数据真的都要反映到 UserComponent 的 state.user 上吗?这样合适吗?
  • 2、如果登录报错了,怎么通知 UserComponent ,怎么告诉其错误信息?

想了想,有种方案:组织好 trigger 返回的数据结构,比如像这样:

{
  actionType: 'login',                  // 本次 action 的类型
  status: 0,                                 // 0代表出错了,1代表成功了
  message: 'an error occurred'  // 错误信息
}

但是转念一想,这明显不对,肯定不是标准的用法,这样的话我又得在 component 中写好多代码来分析这些分发复杂的情况,太不优雅了。

想了半天,实在没想出好的方式,在《聊一聊基于Flux的前端系统》中也没找到相关内容。

于是,使用 Reflux 的想法再次被搁置,继续使用 service 吧!

第三步:别扭的方式解决出错处理

改回 service 之后,心中还是蛮不爽的,便去一个牛人云集的 React 群(161461760)求助,初步描述完我的问题之后,群中一位热心网友提出了他的方式:给 store 添加方法,获取 action 执行的结果。

感觉这种方式似乎能解决问题,虽然还是有点别扭,于是代码变成了这样:

var Reflux = require('reflux');
var React = require('react');

var UserAction = Reflux.createActions({
    'login': {children: ['success', 'failed']}
});

UsersAction.login.listen(function(data) {
    $.post('/api/users/Action/login', data).then(this.success, this.failed);
});

var userStoreMixin = {
    getLoginResult() {
        if (this._error) {
            throw this._error;
        }
        return this._user;
    }
};
var UserStore = Reflux.createStore({
    listenables: UserAction,
    mixins: [userStoreMixin],
    onLoginSuccess(payload) {
        this._error = null;
        this.trigger(payload);
    },
    onLoginFailed(error) {
        if (error.status === -1) {
            this._error = new UnloginError(error.message);
        } else {
            this._error = new Error(error.message);
        }
        this.trigger();
    }
});

var UserComponent = React.createClass({
    mixins: [Reflux.listenTo(UserStore, 'onUserStore')],
    onUserStore() {
        try {
            this.setState({
                user: UserStore.getLoginResult()
            });
        } catch (e) {
            if (e instanceof UnloginError) {
                alert('not login');
            } else {
                alert(e.message);
            }
        }
    },
    render() {
        return <span>{this.state.user.name}</span>;
    }
});

似乎还行,于是开开心心地翻新项目代码,将 service 改成“这种的 Reflux ”。

第四步:产生新的想法

按照第三步的思维使用了一段时间之后,感觉实在是别扭,越来越感受到这不是标准的方案,写出来的代码看着有点丑。

于是想啊想,突然,灵光一闪,还是应该回归到第二步中写的那个例子啊,store 应该只是用来处理正确的数据,至于那些报错什么的,可以用额外的 action 、 store 来处理啊!于是上述代码应该是这个样子的:

var Reflux = require('reflux');
var React = require('react');

var UserAction = Reflux.createActions({
    'login': {children: ['success', 'failed']}
});

UsersAction.login.listen(function(data) {
    $.post('/api/users/Action/login', data).then(this.success, this.failed);
});

var ErrorAction = Reflux.createActions({
    Unlogin: {}, // 未登录
    error: {}    // 一般性的错误
});

var UserStore = Reflux.createStore({
    listenables: UserAction,
    onLoginSuccess(payload) {
        this.trigger(payload);
    },
    onLoginFailed(payload) {
        if (error.status === -1) {
            ErrorAction.Unlogin(error.message);
        } else {
            ErrorAction.error(error.message);
        }
    }
});

var ErrorStoreMixin = {
    UNLOGIN: 1,
    ERROR: 2
};
var ErrorStore = Reflux.createStore({
    listenables: ErrorAction,
    mixins: [ErrorStoreMixin],
    onUnlogin(message) {
        this.trigger({type: this.UNLOGIN, message: message});
    },
    onError(message) {
        this.trigger({type: this.ERROR, message: message});
    }
});

var UserComponent = React.createClass({
    mixins: [Reflux.connect(UserStore, 'user'), Reflux.listenTo(ErrorStore, 'onErrorStore')],
    onErrorStore(error) {
        if (error.type === ErrorStore.UNLOGIN) {
            alert('not login');
        } else if (error.type === ErrorStore.ERROR) {
            alert(error.message);
        }
    },
    render() {
        return <span>{this.state.user.name}</span>;
    }
});

现在,感觉似乎完美一点了,代码看着也相对优雅。

不过,到目前为止,还有一点疑问:按照这种 store 写法,似乎会创建很多 store ,是否需要控制 store 数量,如果有必要,如何整合各个 store ?

带着一些疑问,继续前行吧,骚年!

(后续有使用心得的时候会继续更新本文章)

yibuyisheng avatar May 22 '15 04:05 yibuyisheng

mark

Hi-Rube avatar May 22 '15 04:05 Hi-Rube

mark

ittce avatar May 22 '15 05:05 ittce

mark

fuxiaohei avatar May 23 '15 14:05 fuxiaohei

mark

wengqi avatar May 26 '15 06:05 wengqi

mark

chollaflower avatar May 26 '15 06:05 chollaflower

store中调用action会不会 紧耦合了?

luqin avatar May 27 '15 01:05 luqin

@luqin 还好吧,自己掌控好了应该关系不大的。

yibuyisheng avatar May 27 '15 13:05 yibuyisheng

mark~

lonelylonelygod avatar Jul 09 '15 03:07 lonelylonelygod

希望继续更新!

cike8899 avatar Jul 29 '15 08:07 cike8899

@yibuyisheng 来个ES6版本吧

luqin avatar Jul 31 '15 01:07 luqin

期待更新

boleixiongdi avatar Aug 11 '15 08:08 boleixiongdi

兄弟们,最近由于项目紧,又专注于《 Exploring ES6 》的翻译,因此此篇博客可能不会继续更新了,当然某个心血来潮的时刻也可能会更新哟 ^_^ !

yibuyisheng avatar Aug 11 '15 14:08 yibuyisheng

@yibuyisheng 然而你心血来潮回复了一下~

Hi-Rube avatar Aug 11 '15 15:08 Hi-Rube

该开始 redux 了,哈哈

luqin avatar Aug 11 '15 15:08 luqin