关于Flux
应用的学习例子。
$ git clone https://github.com/ipluser/react-flux-demo.git
$ cd react-flux-demo
$ npm install
$ npm start
浏览器将会自动打开一个新的网页标签,如果没有打开,请直接访问 http://127.0.0.1:8080
:
Flux
应用主要分为四个主要的部分:Views
,Actions
,Dispatcher
,Stores
。
名称 | 描述 |
---|---|
Views | 视图层。 |
Actions | 行为动作层,视图层触发的动作,例如click event 。 |
Dispatcher | 分发中心,注册/接受动作,管理数据流向。 |
Stores | 数据层,管理应用状态,广播通知Views 状态发生改变。 |
单向数据流是Flux
应用的核心。Dispatcher
,Stores
,Views
是独立的输入和输出节点,而Action是一个包含数据和动作类型的简单对象。
打开项目入口文件main.jsx:
// public/scripts/main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import TodoController from './components/todoController.jsx';
ReactDOM.render(<TodoController />, document.body);
此处代码中采用了ReactJS Controller View模式,一个Controller View
是应用中最顶层的组件,它管理着所有应用状态,并以属性方式传递给子组件。接下来我们看看todoController.jsx:
// public/scripts/components/todoController.jsx
import React from 'react';
import TodoAction from '../actions/todoAction.js';
import TodoStore from '../stores/todoStore.js';
import Todo from './todo.jsx';
export default class TodoController extends React.Component {
constructor(props) {
super(props);
}
newItem() {
TodoAction.addItem('new item');
}
render() {
return <Todo newItem={this.newItem} />;
}
}
正如你所看到的,TodoController仅仅只给Todo指定了动作。Todo组件接收属性和渲染:
// public/scripts/components/todo.jsx
import React from 'react';
import '../../styles/components/todo.scss';
export default function Todo(props) {
let list = props.items.map((item, index) => {
return <li className="color--red" key={index}>{item}</li>;
});
return (
<div className="todo">
<ul>{list}</ul>
<button className="todo__click-btn" onClick={props.newItem}>Todo</button>
</div>
);
}
一旦点击todo按钮,TodoController将会触发addItem动作。
TodoAction将数据和动作类型传递给Dispatcher去分发数据流:
// public/scripts/actions/todoAction.js
import AppDispatcher from '../dispatcher.js';
import TodoConstant from '../constants/todoConstant.js';
class TodoAction {
addItem(text) {
AppDispatcher.dispatch({
actionType: TodoConstant.ADD_ITEM,
text
});
}
}
export default new TodoAction();
todoConstant.js是一个包含所有动作类型的常量对象:
// public/scripts/constants/todoConstant.js
export default {
ADD_ITEM: 'TODO_ADD_ITEM'
};
Dispatcher是一个分发中心,它管理着应用的所有数据流向。每一个Store在这里注册,同时提供一个回调函数:
// public/scripts/dispatcher.js
import { Dispatcher } from 'flux';
import TodoStore from './stores/todoStore';
import TodoConstant from './constants/todoConstant';
const AppDispatcher = new Dispatcher();
TodoStore.dispatchToken = AppDispatcher.register(payload => {
switch (payload.actionType) {
case TodoConstant.ADD_ITEM:
TodoStore.addItem(payload.text);
break;
default:
}
});
export default AppDispatcher;
上面代码中可以看到,当TodoAction告诉Dispatcher
发生了一个动作时,TodoStore将会通过注册时的回调函数接受动作的行为。
TodoStore包含状态和业务逻辑。它的职责有点类似MVC中的model:
// public/scripts/stores/todoStore.js
import EventEmitter from 'events';
class TodoStore extends EventEmitter {
constructor() {
super();
this.items = [];
}
getAll() {
return this.items;
}
addItem(text) {
this.items.push(text);
this.change();
}
change() {
this.emit('change');
}
addListener(name, callback) {
this.on(name, callback);
}
removeListener(name, callback) {
this.removeListener(name, callback);
}
}
export default new TodoStore();
当状态发生改变时,TodoStore将会通过事件形式通知Views。
再回到TodoController中,我们初始化应用的状态,同时监听Store
的状态改变事件:
// public/scripts/components/todoController.jsx
import React from 'react';
import TodoAction from '../actions/todoAction.js';
import TodoStore from '../stores/todoStore.js';
import Todo from './todo.jsx';
export default class TodoController extends React.Component {
constructor(props) {
super(props);
this.state = { items: TodoStore.getAll() };
this.onListChange = this.onListChange.bind(this);
}
componentDidMount() {
TodoStore.addListener('change', this.onListChange);
}
componentWillUnmount() {
TodoStore.removeListener('change', this.onListChange);
}
onListChange() {
this.setState({
items: TodoStore.getAll()
});
}
newItem() {
TodoAction.addItem('new item');
}
render() {
return <Todo items={this.state.items} newItem={this.newItem} />;
}
}
一旦TodoController接受到应用状态改变,将会触发Todo
重新渲染。