[TypeScript]使用TypeScript寫Flux

  • 422
  • 0
  • 2016-03-12

既然已經可以使用TypeScript寫React了-[TypeScript]使用TypeScript寫React.JS,用TypeScript寫React的Flux應該不會是太大的問題,所以又繼續的寫了一個Flux的Lab。這個Lab延續使用[React]React.JS學習筆記(5) - 實作一個Flux Example的Example,只是語法換成TypeScript而已。

[TypeScript]使用TypeScript寫Flux

既然已經可以使用TypeScript寫React了-[TypeScript]使用TypeScript寫React.JS,用TypeScript寫React的Flux應該不會是太大的問題,所以又繼續的寫了一個Flux的Lab。這個Lab延續使用[React]React.JS學習筆記(5) - 實作一個Flux Example的Example,只是語法換成TypeScript而已。

Dispatcher & Constants

Dispatcher的程式很簡單,只是把Dispatcher物件建立起來而已。

dispatcher/AppDispatcher.tsx

import * as Flux from 'flux';
var Dispatcher = new Flux.Dispatcher();
export default Dispatcher;

而Constants就有使用到TypeScript的特性 - Enum,以取代傳統使用字串物件的方式。

constants/ActionConstants.tsx

enum ActionConstants {
    TODO_CREATE,
}
export default ActionConstants;

Action

因為Constants使用Enum,所以可以看到,當Action在進行dispatch的時候,ActionType就不是使用字串HardCode的方式了。

actions/TodoAction.tsx

import AppDispatcher from '../dispatcher/AppDispatcher';
import ActionConstants from '../constants/ActionConstants';

class TodoAction {
    createTodo(inTodoText) {
        AppDispatcher.dispatch({
            actionType: ActionConstants.TODO_CREATE,
            text: inTodoText
        });
    }
}

export default TodoAction;

Store

接下來就是Flux中比較核心的的Store,Store做的事情主要有兩個,一個是處理不同的Action的邏輯,另一個是處理資料變動後,要觸發事件(Event)讓View可以知道。

透過Dispatcher處理Action的邏輯

Constants的Enum型別在這裡就發揮得很好,在switch語句中就看不到字串HardCode的情況,直接用點語法帶出- ActionConstants.TODO_CREATE

var TodoStoreObj: TodoStore = new TodoStore();

AppDispatcher.register(function (action: ITodoAction): void {
    var text: string;

    switch (action.actionType) {
        case ActionConstants.TODO_CREATE:
            createTodo(action.text);
            TodoStoreObj.emitChange();
            break;

        default:
            break;
    }
});

Store的Event

之前是使用object-assign的方式將EventEmitter的function與我們自己寫的Store放在一起。使用TypeScript來寫,就可以透過繼承的方式做到相同的效果,這樣的寫法所傳達出的語意也比較清楚直接。

class TodoStore extends EventEmitter {
    getTodoItems() {
        return todoItems;
    }

    emitChange() {
        this.emit(CHANGE_EVENT);
    }

    addChangeListener(inCallback) {
        this.on(CHANGE_EVENT, inCallback);    
    }

    removeChangeListener(inCacllback) {
        this.removeListener(CHANGE_EVENT, inCacllback);
    }
}

View

前面的苦工做完後,View就簡單了,但我還是踩了一些坑。主要有兩個,一個是refs的存取,要使用以下的語法。因為我使用的React是V0.14.7的版本,所以findDOMNode原本由React換到ReactDOM了:

ReactDOM.findDOMNode<HTMLInputElement>(this.refs["txtT"]).value

另外一個是在HTML元件的事件綁定到function時,需要使用bind()this傳進,才有辦法正確取得React的refs, state, props物件。

<button onClick={this.handleAddTodo.bind(this)}>Add</button>

components/TodoApp.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import TodoStore from '../stores/TodoStore';
import TodoAction from '../actions/TodoAction';
import TodoAppList from './TodoAppList';

export default class TodoApp extends React.Component<{}, {}> {
    //********** React Component LifeCycle **********
    constructor(props) {
        super(props);
        this.state = { todoItems: TodoStore.getTodoItems() };
    }

    componentDidMount() {
        TodoStore.addChangeListener(this.changeHandler.bind(this));
    }

    componentWillUnmount() {
        TodoStore.removeChangeListener(this.changeHandler.bind(this));
    }

    //********** Features **********
    changeHandler() {
        this.setState({ todoItems: TodoStore.getTodoItems() });
    }

    public handleAddTodo() {
        var newTodo = ReactDOM.findDOMNode<HTMLInputElement>(this.refs["txtT"]).value;
        new TodoAction().createTodo(newTodo);
        ReactDOM.findDOMNode<HTMLInputElement>(this.refs["txtT"]).value = '';
    }

    //********** DOM **********
    render() {
        return (
            <div>
                <h1>Todo</h1>
                <input type='text' ref="txtT"></input>
                <button onClick={this.handleAddTodo.bind(this)}>Add</button>
                <TodoAppList Items={TodoStore.getTodoItems()}/>
            </div>

        );
    }
}

環境設定

完整的Example Code放在GitHub上,下載下來後,依據底下的步驟進行套件的安裝及TypeScript的編譯。

Package Installation

安裝tsd後,執行以下的Command下載TypeScript定義檔

tsd install

安裝npm後,執行以下的Command進行package的安裝

npm install

Build script

透過編輯器進行編譯TypeScript,或是執行以下Command進行TypeScript編譯

tsc

有使用browserify,故執行以下的npm 命令打包js

npm start

Demo

執行npm start後會產生bundle.js,這時直接開啟index.html就可以看到效果