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

相關資源


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

Ref: 詳解React Flux架構工做方式git

Ref: 阮一峯 github

 

*** Redux 和 Flux 很像 ***npm

主要區別在於Flux有多個能夠改變應用狀態的store,它經過事件來觸發這些變化。【store較多】redux

組件能夠訂閱這些事件來和當前狀態同步。架構

    • Redux 沒有分發器dispatcher,
    • Flux 中dispatcher被用來傳遞數據到註冊的回調事件。

另外一個不一樣是Flux中有不少擴展是可用的,這也帶來了一些混亂與矛盾。併發

 

*** 下一步 ***框架

(1) Reduxless

Goto: 《看漫畫,學 Redux》

你覺得Redux就是終結了麼? NO, NO, NO!

 

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

redux-saga 是一個 redux 的中間件,而中間件的做用是爲 redux 提供額外的功能。

聊一聊 redux 異步流之 redux-saga

 

 

 

框架結構


Flux將一個應用分紅四個部分,對,咱們故意將代碼寫成這種單向流的邏輯形式。

  • View: 視圖層
  • Action(動做):視圖層發出的消息(好比mouseClick)
  • Dispatcher(派發器):用來接收Actions、執行回調函數
  • Store(數據層):用來存放應用的狀態,一旦發生變更,就提醒Views要更新頁面

 

$ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git
$ cd extremely-simple-flux-demo && npm install
$ npm start
示範代碼下載

 

 

 

代碼演示


1、"controll view" 模式

  • (1) MyButton不包含狀態,是一個純組件,從而方便了測試和複用。
  • (2) MyButtonController,只保存狀態,將參數傳給子組件MyButton。

 

Ref: React中的無狀態和有狀態組件

無狀態組件:無狀態組件(Stateless Component)是最基礎的組件形式,因爲沒有狀態的影響因此就是純靜態展現的做用。通常來講,各類UI庫裏也是最開始會開發的組件類別。如按鈕、標籤、輸入框等。它的基本組成結構就是屬性(props)加上一個渲染函數(render)。因爲不涉及到狀態的更新,因此這種組件的複用性也最強。

有狀態組件:在無狀態組件的基礎上,若是組件內部包含狀態(state)且狀態隨着事件或者外部的消息而發生改變的時候,這就構成了有狀態組件(Stateful Component)。有狀態組件一般會帶有生命週期(lifecycle),用以在不一樣的時刻觸發狀態的更新。這種組件也是一般在寫業務邏輯中最常常使用到的,根據不一樣的業務場景組件的狀態數量以及生命週期機制也不盡相同。

 

button如何跟這個狀態扯上了關係?思考下若是本身實現會採用怎樣的思路?

 

 2、代碼解讀

(1) 可見是個「純組件」的代碼,方便獨立測試!

一旦用戶點擊,就調用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');    // ----> 調用方法,會觸發名爲的Action。 }, render: function() { return <MyButton onClick={this.createNewItem}   // <---- 賦具體的onClick,也便是幹活的地方 />; } }); module.exports = MyButtonController;createNewItemaddNewItem

找到了幹活的地方(改變狀態的地方),但具體的事該怎麼去作,是交給了下面的action來處理,也便是發了「一道命令」。

 

 

(3) 把動做ADD_NEW_ITEM派發到Store,看上去:dispatch就會與switch緊密相連。

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

var ButtonActions = {
   addNewItem: function (text) {
      AppDispatcher.dispatch({      // ButtonActions.addNewItem方法,使用,把動做派發到Store
        actionType: 'ADD_NEW_ITEM',
        text: text
      });
    },
};

module.exports = ButtonActions;AppDispatcherADD_NEW_ITEM

 

 

(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.emitChange();   // 觸發「change」事件,在哪裏監聽到了呢? break; default: // no op } }) module.exports = AppDispatcher;ListStore

可見,來什麼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;
相關文章
相關標籤/搜索