{"action":"created","issue":{"url":"https://api.github.com/repos/yibuyisheng/blogs/issues/2","labels_url":"https://api.github.com/repos/yibuyisheng/blogs/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/yibuyisheng/blogs/issues/2/comments","events_url":"https://api.github.com/repos/yibuyisheng/blogs/issues/2/events","html_url":"https://github.com/yibuyisheng/blogs/issues/2","id":79275665,"number":2,"title":"Reflux 使用进化日记","user":{"login":"yibuyisheng","id":2581682,"avatar_url":"https://avatars.githubusercontent.com/u/2581682?v=3","gravatar_id":"","url":"https://api.github.com/users/yibuyisheng","html_url":"https://github.com/yibuyisheng","followers_url":"https://api.github.com/users/yibuyisheng/followers","following_url":"https://api.github.com/users/yibuyisheng/following{/other_user}","gists_url":"https://api.github.com/users/yibuyisheng/gists{/gist_id}","starred_url":"https://api.github.com/users/yibuyisheng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yibuyisheng/subscriptions","organizations_url":"https://api.github.com/users/yibuyisheng/orgs","repos_url":"https://api.github.com/users/yibuyisheng/repos","events_url":"https://api.github.com/users/yibuyisheng/events{/privacy}","received_events_url":"https://api.github.com/users/yibuyisheng/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-05-22T04:17:11Z","updated_at":"2015-05-27T01:34:51Z","closed_at":null,"body":"Reflux 算是比较新的东西，由于自己水平有限，刚接触，不能很好地去使用 Reflux 来处理数据，下面是我使用 Reflux 逐步进化的过程（当然最终状态不一定就是标准的 Reflux 使用方式）：\r\n\r\n## 第一步：初识 Reflux\r\n\r\n一直在听人说 Reflux ，说这个东西比较适合中小型的前端项目，使用起来很方便，于是我就找到了 Reflux  在 GitHub 的[主页](https://github.com/spoike/refluxjs)。\r\n\r\n文档说 dispatcher 被移除了，没关系，反正我也没用过 Flux 。\r\n\r\n于是继续阅读关于 actions 和 stores 的文档。由于心浮气躁急着用，看文档很马虎，action 、 store 可以监听过去监听过来的，还有 store 可以 connect 啥的，完全看晕了，无法用 Reflux 组织起一个完整的处理流程。但是没关系，我就按照文档上的这些 listen 啥的，自己来写写看吧。\r\n\r\n于是， 创建 action ，在 store 中用 listenTo 来监听 action ，然后请求数据，store trigger 返回数据。写的时候，由于完全不理解 Reflux 怎么用，一通胡乱监听，写出来的代码不三不四，看着都觉得累。为了照顾项目进度，放弃 Reflux ，自己写一个 service 层吧。\r\n\r\n## 第二步：认识了一点 Reflux\r\n\r\n过了几天，对 Reflux 心有不甘，于是转头再去看 Reflux 文档，同时也很开心找到一篇[使用 Reflux 的经验文章](http://react.nodejs-china.org/t/liao-liao-ji-yu-fluxde-qian-duan-xi-tong/615)，于是知道了 action 可以当成方法调用，在 action 中监听调用，发出请求之类的，然后 store 做一些存储等操作，再 trigger ，component 中通过 mixin 来监听 store 中的 trigger ，然后做一些界面变动，摘录一段那篇文章中的例子：\r\n\r\n```js\r\nvar Reflux = require('reflux');\r\nvar React = require('react');\r\n\r\nvar UserAction = Reflux.createAction({\r\n    'login': {children: ['success', 'failed']}\r\n});\r\n\r\nUsersAction.login.listen(function(data) {\r\n    $.post('/api/users/Action/login', data).then(this.success, this.failed);\r\n});\r\n\r\nvar UserStore = Reflux.createStore({\r\n    listenables: UserAction,\r\n    onLoginSuccess: function(payload) {\r\n        this.trigger(payload);\r\n    },\r\n    onLoginFailed: function(payload) {\r\n        this.trigger(payload);\r\n    }\r\n});\r\n\r\nvar UserComponent = React.createClass({\r\n    mixins: [Reflux.connect(UserStore, 'user')],\r\n    render: function() {\r\n        return <span>{this.state.user.name}</span>;\r\n    }\r\n});\r\n```\r\n\r\n感觉自己似乎知道怎么来组织流程了，于是很开心地又去改造代码，希望能用上 Reflux 。\r\n\r\n写了一会儿，发现完了，因为有这样的场景：就拿上述一小段代码来说，UserAction 中很可能还有其它 action ，例如：\r\n\r\n```js\r\nvar UserAction = Reflux.createActions({\r\n    'login': {children: ['success', 'failed']},\r\n    'register': {children: ['success', 'failed']}\r\n});\r\n```\r\n\r\nlogin 和 register 两个 action 都会触发 UserStore 中相应方法的调用，然后这些方法再调用 trigger ，然后改变 UserComponent 中 `state.user` 的值，此处有两个问题：\r\n\r\n* 1、登录和注册最终得到的数据真的都要反映到 UserComponent 的 `state.user` 上吗？这样合适吗？\r\n* 2、如果登录报错了，怎么通知 UserComponent ，怎么告诉其错误信息？\r\n\r\n想了想，有种方案：组织好 trigger 返回的数据结构，比如像这样：\r\n\r\n```js\r\n{\r\n  actionType: 'login',                  // 本次 action 的类型\r\n  status: 0,                                 // 0代表出错了，1代表成功了\r\n  message: 'an error occurred'  // 错误信息\r\n}\r\n```\r\n\r\n但是转念一想，这明显不对，肯定不是标准的用法，这样的话我又得在 component 中写好多代码来分析这些分发复杂的情况，太不优雅了。\r\n\r\n想了半天，实在没想出好的方式，在[《聊一聊基于Flux的前端系统》](http://react.nodejs-china.org/t/liao-liao-ji-yu-fluxde-qian-duan-xi-tong/615)中也没找到相关内容。\r\n\r\n于是，使用 Reflux 的想法再次被搁置，继续使用 service 吧！\r\n\r\n## 第三步：别扭的方式解决出错处理\r\n\r\n改回 service 之后，心中还是蛮不爽的，便去一个牛人云集的 React 群(161461760)求助，初步描述完我的问题之后，群中一位热心网友提出了他的方式：给 store 添加方法，获取 action 执行的结果。\r\n\r\n感觉这种方式似乎能解决问题，虽然还是有点别扭，于是代码变成了这样：\r\n\r\n```js\r\nvar Reflux = require('reflux');\r\nvar React = require('react');\r\n\r\nvar UserAction = Reflux.createActions({\r\n    'login': {children: ['success', 'failed']}\r\n});\r\n\r\nUsersAction.login.listen(function(data) {\r\n    $.post('/api/users/Action/login', data).then(this.success, this.failed);\r\n});\r\n\r\nvar userStoreMixin = {\r\n    getLoginResult() {\r\n        if (this._error) {\r\n            throw this._error;\r\n        }\r\n        return this._user;\r\n    }\r\n};\r\nvar UserStore = Reflux.createStore({\r\n    listenables: UserAction,\r\n    mixins: [userStoreMixin],\r\n    onLoginSuccess(payload) {\r\n        this._error = null;\r\n        this.trigger(payload);\r\n    },\r\n    onLoginFailed(error) {\r\n        if (error.status === -1) {\r\n            this._error = new UnloginError(error.message);\r\n        } else {\r\n            this._error = new Error(error.message);\r\n        }\r\n        this.trigger();\r\n    }\r\n});\r\n\r\nvar UserComponent = React.createClass({\r\n    mixins: [Reflux.listenTo(UserStore, 'onUserStore')],\r\n    onUserStore() {\r\n        try {\r\n            this.setState({\r\n                user: UserStore.getLoginResult()\r\n            });\r\n        } catch (e) {\r\n            if (e instanceof UnloginError) {\r\n                alert('not login');\r\n            } else {\r\n                alert(e.message);\r\n            }\r\n        }\r\n    },\r\n    render() {\r\n        return <span>{this.state.user.name}</span>;\r\n    }\r\n});\r\n```\r\n\r\n似乎还行，于是开开心心地翻新项目代码，将 service 改成“这种的 Reflux ”。\r\n\r\n## 第四步：产生新的想法\r\n\r\n按照第三步的思维使用了一段时间之后，感觉实在是别扭，越来越感受到这不是标准的方案，写出来的代码看着有点丑。\r\n\r\n于是想啊想，突然，灵光一闪，还是应该回归到第二步中写的那个例子啊，store 应该只是用来处理`正确的数据`，至于那些报错什么的，可以用额外的 action 、 store 来处理啊！于是上述代码应该是这个样子的：\r\n\r\n```js\r\nvar Reflux = require('reflux');\r\nvar React = require('react');\r\n\r\nvar UserAction = Reflux.createActions({\r\n    'login': {children: ['success', 'failed']}\r\n});\r\n\r\nUsersAction.login.listen(function(data) {\r\n    $.post('/api/users/Action/login', data).then(this.success, this.failed);\r\n});\r\n\r\nvar ErrorAction = Reflux.createActions({\r\n    Unlogin: {}, // 未登录\r\n    error: {}    // 一般性的错误\r\n});\r\n\r\nvar UserStore = Reflux.createStore({\r\n    listenables: UserAction,\r\n    onLoginSuccess(payload) {\r\n        this.trigger(payload);\r\n    },\r\n    onLoginFailed(payload) {\r\n        if (error.status === -1) {\r\n            ErrorAction.Unlogin(error.message);\r\n        } else {\r\n            ErrorAction.error(error.message);\r\n        }\r\n    }\r\n});\r\n\r\nvar ErrorStoreMixin = {\r\n    UNLOGIN: 1,\r\n    ERROR: 2\r\n};\r\nvar ErrorStore = Reflux.createStore({\r\n    listenables: ErrorAction,\r\n    mixins: [ErrorStoreMixin],\r\n    onUnlogin(message) {\r\n        this.trigger({type: this.UNLOGIN, message: message});\r\n    },\r\n    onError(message) {\r\n        this.trigger({type: this.ERROR, message: message});\r\n    }\r\n});\r\n\r\nvar UserComponent = React.createClass({\r\n    mixins: [Reflux.connect(UserStore, 'user'), Reflux.listenTo(ErrorStore, 'onErrorStore')],\r\n    onErrorStore(error) {\r\n        if (error.type === ErrorStore.UNLOGIN) {\r\n            alert('not login');\r\n        } else if (error.type === ErrorStore.ERROR) {\r\n            alert(error.message);\r\n        }\r\n    },\r\n    render() {\r\n        return <span>{this.state.user.name}</span>;\r\n    }\r\n});\r\n```\r\n\r\n现在，感觉似乎完美一点了，代码看着也相对优雅。\r\n\r\n不过，到目前为止，还有一点疑问：按照这种 store 写法，似乎会创建很多 store ，是否需要控制 store 数量，如果有必要，如何整合各个 store ？\r\n\r\n带着一些疑问，继续前行吧，骚年！\r\n\r\n（后续有使用心得的时候会继续更新本文章）"},"comment":{"url":"https://api.github.com/repos/yibuyisheng/blogs/issues/comments/105714743","html_url":"https://github.com/yibuyisheng/blogs/issues/2#issuecomment-105714743","issue_url":"https://api.github.com/repos/yibuyisheng/blogs/issues/2","id":105714743,"user":{"login":"luqin","id":2267688,"avatar_url":"https://avatars.githubusercontent.com/u/2267688?v=3","gravatar_id":"","url":"https://api.github.com/users/luqin","html_url":"https://github.com/luqin","followers_url":"https://api.github.com/users/luqin/followers","following_url":"https://api.github.com/users/luqin/following{/other_user}","gists_url":"https://api.github.com/users/luqin/gists{/gist_id}","starred_url":"https://api.github.com/users/luqin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/luqin/subscriptions","organizations_url":"https://api.github.com/users/luqin/orgs","repos_url":"https://api.github.com/users/luqin/repos","events_url":"https://api.github.com/users/luqin/events{/privacy}","received_events_url":"https://api.github.com/users/luqin/received_events","type":"User","site_admin":false},"created_at":"2015-05-27T01:34:51Z","updated_at":"2015-05-27T01:34:51Z","body":"store中调用action会不会 紧耦合了？"}}