flux demo todomvc
look
看index.html
文件只有用到js/bundle.js
文件, 然后bundle.js
哪里来, 看看package.json
,
"scripts": {
"start": "watchify -o js/bundle.js -v -d js/app.js",
"build": "browserify . -t [envify --NODE_ENV production] | uglifyjs -cm > js/bundle.min.js",
"test": "jest"
}
app.js
作为入口, 然后监视, 一旦发生改变就回编译到 bundle.js
.
然后接着看, app.js
内容:
var React = require('react');
var TodoApp = require('./components/TodoApp.react'); // here
React.render(
<TodoApp />,
document.getElementById('todoapp')
);
一个TodoApp
搞定一切. 至于它何来头这就要开篇讲了.
创建一个 dispatcher
创建一个 dispatcher, 下面是一个简单例子,使用了 javascript 的 promises
var Promise = require('es6-promise').Promise;
var assign = require('object-assign');
var _callbacks = [];
var _promises = [];
var Dispatcher = function() {};
Dispatcher.prototype = assign({}, Dispatcher.prototype, {
/**
* Register a Store's callback so that it may be invoked by an action.
* @param {function} callback The callback to be registered.
* @return {number} The index of the callback within the _callbacks array.
*/
register: function(callback) {
_callbacks.push(callback);
return _callbacks.length - 1; // index
},
/**
* dispatch
* @param {object} payload The data from the action.
*/
dispatch: function(payload) {
// First create array of promises for callbacks to reference.
var resolves = [];
var rejects = [];
_promises = _callbacks.map(function(_, i) {
return new Promise(function(resolve, reject) {
resolves[i] = resolve;
rejects[i] = reject;
});
});
// Dispatch to callbacks and resolve/reject promises.
_callbacks.forEach(function(callback, i) {
// Callback can return an obj, to resolve, or a promise, to chain.
// See waitFor() for why this might be useful.
Promise.resolve(callback(payload)).then(function() {
resolves[i](payload);
}, function() {
rejects[i](new Error('Dispatcher callback unsuccessful'));
});
});
_promises = [];
}
});
module.exports = Dispatcher;
这个dispatch
只公开了2个方法register()
和dispatch()
,我们将会使用register()
来给store
注册回调函数, 通过dispatch()
来触发这些 action 完成回调.
那好我们来看看下面代码:
var Dispatcher = require('./Dispatcher');
var assign = require('object-assign');
var AppDispatcher = assign({}, Dispatcher.prototype, {
/**
* A bridge function between the views and the dispatcher, marking the action
* as a view action. Another variant here could be handleServerAction.
* @param {object} action The data coming from the view.
*/
handleViewAction: function(action) {
this.dispatch({
source: 'VIEW_ACTION',
action: action
});
}
});
module.exports = AppDispatcher;
创建 stores
我们可以通过node
的EventEmitter
来创建store
, 我们需要通过EventEmitter
来广播change
事件给相关的controller-view
来看看到底长啥样,
var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var TodoConstants = require('../constants/TodoConstants');
var assign = require('object-assign');
var CHANGE_EVENT = 'change';
var _todos = {}; // collection of todo items
/**
* Create a TODO item.
* @param {string} text The content of the TODO
*/
function create(text) {
// Using the current timestamp in place of a real id.
var id = Date.now();
_todos[id] = {
id: id,
complete: false,
text: text
};
}
/**
* Delete a TODO item.
* @param {string} id
*/
function destroy(id) {
delete _todos[id];
}
// 继承了 EventEmitter
var TodoStore = assign({}, EventEmitter.prototype, {
/**
* Get the entire collection of TODOs.
* @return {object}
*/
getAll: function() {
return _todos;
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
/**
* @param {function} callback
*/
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
/**
* @param {function} callback
*/
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
},
// 注册 action
dispatcherIndex: AppDispatcher.register(function(payload) {
var action = payload.action;
var text;
switch(action.actionType) {
case TodoConstants.TODO_CREATE:
text = action.text.trim();
if (text !== '') {
create(text);
TodoStore.emitChange();
}
break;
case TodoConstants.TODO_DESTROY:
destroy(action.id);
TodoStore.emitChange();
break;
// add more cases for other actionTypes, like TODO_UPDATE, etc.
}
return true; // No errors. Needed by promise in Dispatcher.
})
});
module.exports = TodoStore;
上面代码里有些地方需要说明下, 从最开始的时候我们创建了一个_ tods
的私有对象,这个对象包含了所有的单独todi
list 的 item, 因为它在 class 外面存在. 但是却它却却在于 module 的这个闭包
里, 这样它就不可以在 module 意外的地方呗修改, 这样就保证了一个直接进出的接口给我们的数据流,保证只有在通过 action 的情况下才可以修改数据.
另外一个地方就是注册 action 的回调函数到dispatcher
上,我们把气球的数据交给个回调函数来处理,并且把它注册在 dispatcher 里.当前我们只负责两个不同的actiontype
. 之后我们可以添加更多.
通过controller-view
来监听修改事件
我们需要一个位于组建体系的最上层的React 组件
来监听所有的change
事件.在大型应用中我们可能需要更多的组件来监听.可能一个页面里每一个 section 都需要一个监听组件. 下面来看看简单的代码示例, 完整的可以查看github repo todomvc
.
var Footer = require('./Footer.react');
var Header = require('./Header.react');
var MainSection = require('./MainSection.react');
var React = require('react');
var TodoStore = require('../stores/TodoStore');
function getTodoState() {
return {
allTodos: TodoStore.getAll()
};
}
var TodoApp = React.createClass({
getInitialState: function() {
return getTodoState();
},
componentDidMount: function() {
TodoStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
TodoStore.removeChangeListener(this._onChange);
},
/**
* @return {object}
*/
render: function() {
return (
<div>
<Header />
<MainSection
allTodos={this.state.allTodos}
areAllComplete={this.state.areAllComplete}
/>
<Footer allTodos={this.state.allTodos} />
</div>
);
},
_onChange: function() {
this.setState(getTodoState());
}
});
module.exports = TodoApp;
到这里,我们已经进入 React的世界,通过使用 react 的生命周期方法,通过getInitialState()
初始化这个controller-vew
, 然后通过componentDidMount()
来完成事件监听, 然后通过componentWillUnmount()
来完成清理. 我们渲染了一个div
然后把从 store 哪里拿到的数据填充进去.
关于视图
站在更高一些的角度,我们可以把当前的 React 组件系统理解成如下的结构:
<TodoApp>
<Header>
<TodoTextInput />
</Header>
<MainSection>
<ul>
<TodoItem />
</ul>
</MainSection>
</TodoApp>
如果当前进入一个编辑模式,它还会渲染一个TodoTextInput
的子类, 让我们来看看这些组件是如何从props
哪里拿到组件然后展示他们的, 然后还有他们如何在 action 和 dispatcher 之间交互通信. 这个MainSection
需要遍历todo item
从 Todoapp 那拿来的 list. 在组件的render()
方法里我们可以这样做:
var allTodos = this.props.allTodos;
for (var key in allTodos) {
todos.push(<TodoItem key={key} todo={allTodos[key]} />);
}
return (
<section id="main">
<ul id="todo-list">{todos}</ul>
</section>
);
到这里, 我们可以展示每一个 item, 通过 id 可以编辑, 同样可以删除一个 item
var React = require('react');
var TodoActions = require('../actions/TodoActions');
var TodoTextInput = require('./TodoTextInput.react');
var TodoItem = React.createClass({
propTypes: {
todo: React.PropTypes.object.isRequired
},
render: function() {
var todo = this.props.todo;
return (
<li
key={todo.id}>
<label>
{todo.text}
</label>
<button className="destroy" onClick={this._onDestroyClick} />
</li>
);
},
_onDestroyClick: function() {
TodoActions.destroy(this.props.todo.id);
}
});
module.exports = TodoItem;
创建一些语义化的动作方法
Author And Source
この問題について(flux demo todomvc), 我々は、より多くの情報をここで見つけました https://qiita.com/pipixia/items/b6ea4a7a9a191d3dd296著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .