[React] 07 - Flux: uni-flow for react

时间:2021-03-15 09:28:42

相关资源


Ref: [Android Module] 03 - Software Design and Architecture

Ref: 详解React Flux架构工作方式

Ref: 阮一峰

*** Redux 和 Flux 很像 ***

主要区别在于Flux有多个可以改变应用状态的store,它通过事件来触发这些变化。【store较多】

组件可以订阅这些事件来和当前状态同步。

    • Redux 没有分发器dispatcher,
    • Flux 中dispatcher被用来传递数据到注册的回调事件。

另一个不同是Flux中有很多扩展是可用的,这也带来了一些混乱与矛盾。

[React] 07 - Flux: uni-flow for react

*** 下一步 ***

(1) Redux

[React] 07 - Flux: uni-flow for react

Goto: 《看漫画,学 Redux》

你以为Redux就是终结了么? NO, NO, NO!

(2) redux-saga又是什么鬼?

redux-saga 是一个 redux 的中间件,而中间件的作用是为 redux 提供额外的功能。

聊一聊 redux 异步流之 redux-saga

框架结构


Flux将一个应用分成四个部分,对,我们故意将代码写成这种单向流的逻辑形式。

  • View: 视图层
  • Action(动作):视图层发出的消息(比如mouseClick)
  • Dispatcher(派发器):用来接收Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

[React] 07 - Flux: uni-flow for react

$ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
$ cd extremely-simple-flux-demo && npm install
$ npm start

示范代码下载

代码演示


一、"controll view" 模式

  • (1) MyButton,不包含状态,是一个纯组件,从而方便了测试和复用。
  • (2) MyButtonController,只保存状态,将参数传给子组件MyButton。

Ref: React中的无状态和有状态组件

无状态组件:无状态组件(Stateless Component)是最基础的组件形式,由于没有状态的影响所以就是纯静态展示的作用。一般来说,各种UI库里也是最开始会开发的组件类别。如按钮、标签、输入框等。它的基本组成结构就是属性(props)加上一个渲染函数(render)。由于不涉及到状态的更新,所以这种组件的复用性也最强。

有状态组件:在无状态组件的基础上,如果组件内部包含状态(state)且状态随着事件或者外部的消息而发生改变的时候,这就构成了有状态组件(Stateful Component)。有状态组件通常会带有生命周期(lifecycle),用以在不同的时刻触发状态的更新。这种组件也是通常在写业务逻辑中最经常使用到的,根据不同的业务场景组件的状态数量以及生命周期机制也不尽相同。

button如何跟这个状态扯上了关系?思考下如果自己实现会采用怎样的思路?

[React] 07 - Flux: uni-flow for react

二、代码解读

(1) 可见是个“纯组件”的代码,方便独立测试!

[React] 07 - Flux: uni-flow for react

一旦用户点击,就调用this.createNewItem 方法,向Dispatcher发出一个Action。

一问:props.onClick在哪里?

答:button的onClick指向了外边的MyButton的onClick的内容,也就是createNewItem。

二问:为什么这么做?

如此,状态就可以独立在button的外边,button就是个“纯组件”了。

(2) 将“状态”转发给子组件,并发一道命令(action) 给store。

// components/MyButtonController.jsx
var React = require('react');
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton'); var MyButtonController = React.createClass({
createNewItem: function (event) {
ButtonActions.addNewItem('new item');    // ----> 调用createNewItem方法,会触发名为addNewItem的Action。
}, render: function() {
return <MyButton
onClick={this.createNewItem}   // <---- 赋具体的onClick,也即是干活的地方
/>;
}
}); module.exports = MyButtonController;

找到了干活的地方(改变状态的地方),但具体的事该怎么去做,是交给了下面的action来处理,也即是发了“一道命令”。

(3) 把动作ADD_NEW_ITEM派发到Store,看上去:dispatch就会与switch紧密相连。

[React] 07 - Flux: uni-flow for react

var AppDispatcher = require('../dispatcher/AppDispatcher');

var ButtonActions = {
 addNewItem: function (text) {
  AppDispatcher.dispatch({      // ButtonActions.addNewItem方法,使用AppDispatcher,把动作ADD_NEW_ITEM派发到Store
  actionType: 'ADD_NEW_ITEM',
  text: text
  });
  },
}; module.exports = ButtonActions;

(4) 看作是一个路由器,负责在 View 和 Store 之间,建立 Action 的正确传递路线。

注意,Dispatcher 只能有一个,而且是全局的。

Jeff:

  这里调用了store的具体改变state的函数,如此,函数其实就都集中写在了store里。

  如何正确的判断request来调用store里的函数,也即是"router"问题,便是dispatch的事儿。

var Dispatcher    = require('flux').Dispatcher;
var AppDispatcher = new Dispatcher();
var ListStore = require('../stores/ListStore');

/**
* 全局性的注册,因为dispatcher只能有一个
*/
AppDispatcher.register(function (action) {
switch(action.actionType) {
case 'ADD_NEW_ITEM':
ListStore.addNewItemHandler(action.text);  // 执行回调函数,对ListStore进行操作
ListStore.emitChange();   // 触发“change”事件,在哪里监听到了呢?
break;
default:
// no op
}
}) module.exports = AppDispatcher;

可见,来什么actionType,就分配对应的函数去干活;具体改变状态的函数在接下来的store里面。

(5) Store 改变 并 保存着整个应用的状态,也就是具体干活的地儿。

// stores/ListStore.js
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');

/**
* Store 需要在变动后向 View 发送"change"事件,因此它必须实现事件接口
*/
var ListStore = assign({}, EventEmitter.prototype, {
items: [], getAll: function () {
return this.items;
}, addNewItemHandler: function (text) {
this.items.push(text);
}, emitChange: function () {
this.emit('change');  // --> 触发事件
},

-------------------------------------------------------
addChangeListener: function(callback) {
this.on('change', callback);  // <-- 监听事件
}, removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
}); module.exports = ListStore;

(6) 补充上View中的监听事件

可见,在(2)的MyButtonController中,还少了什么:监听“change"触发时间的listener。

var React = require('react');
var ListStore = require('../stores/ListStore');        // 添加
var ButtonActions = require('../actions/ButtonActions');
var MyButton = require('./MyButton'); var MyButtonController = React.createClass({ ----------------------------------------------------------
getInitialState: function () {
return {
items: ListStore.getAll()
};
}, componentDidMount: function() {
ListStore.addChangeListener(this._onChange);
}, componentWillUnmount: function() {
ListStore.removeChangeListener(this._onChange);
}, _onChange: function () {      // <---- listener
this.setState({
items: ListStore.getAll()
});
},

----------------------------------------------------------
createNewItem: function (event) {
ButtonActions.addNewItem('new item');
}, render: function() {
return <MyButton
items={this.state.items}      // 添加
onClick={this.createNewItem}
/>;
} }); module.exports = MyButtonController;