手把手教你玩转 Rect 的 flux | 令狐葱 @前端笔记

904 阅读6分钟
原文链接: linghucong.js.org

本文所有代码可以在Github上查看。
react-flux-demo

新建React项目

既然有兴趣打开本文,说明你对React的基本开发应该有所了解。我们教程的第一步就是创建一个新的React项目。我们可以使用一些React Boilerplate项目方便的创建一个新的React项目,比如我之前创建的React Boilerplate:

react_boilerplate

该React Boilerplate的实现过程,可以参考我之前的文章手把手教你基于ES6架构自己的React Boilerplate项目

或者,也可以使用facebook最新推出的一个类似项目:

create-react-app

我们这里使用facebookincubator/create-react-app:

npm install -g create-react-app
create-react-app todoApp
cd todoApp/
npm start

然后我们就可以通过访问http://localhost:3000/来查看结果了。

使用React实现简单的Todo程序(不带flux)

我们要实现一个简单的todo程序,可以添加、删除todo条目。

ListContainer 组件

首先我们需要一个container用于放置todo列表,命名为ListContainer.js,其内容如下:

export default class ListContainer extends Component {
  state = {
    list: []
  }
  handleAddItem = (newItem) => {
    var list = this.state.list;
    list.push(newItem);
    this.setState({
      list: list
    });
  }
  handleRemoveItem = (index) => {
   this.state.list.splice(index, 1);
    this.setState({
      list: this.state.list
    });
  }
  render = () => {
    return (
      

Todo List

) } }

render函数中来组建dom元素显示todo列表,其中AddItem组件是用来新建todo的,List组件用于显示todo列表项。

在两个响应函数handleAddItemhandleRemoveItem中,通过setState函数来管理state。

List 组件

todo列表项显示组件内容如下:

export default class List extends Component {
  static defaultProps = {
          items: [],
          remove: () => {}
  }
  static propTypes = {
      items: React.PropTypes.array.isRequired,
      remove: React.PropTypes.func.isRequired
  }
  render = () => {
    var styles = {
      uList: {
        paddingLeft: 0,
        listStyleType: "none"
      },
      listGroup: {
        margin: '5px 0',
        borderRadius: 5
      },
      removeItem: {
        fontSize: 20,
        float: "left",
        position: "absolute",
        top: 12,
        left: 6,
        cursor: "pointer",
        color: "rgb(222, 79, 79)"
      },
      todoItem: {
        paddingLeft: 20,
        fontSize: 17
      }
    };
    var listItems = this.props.items.map(function(item, index){
      return (
        
  • {item}
  • ) }.bind(this)); return (
      {listItems}
    ) } };

    其中用于删除的图标上的onClick事件会调用父级组件响应的方法,完成删除操作

    AddItem 组件

    添加新的todo项的组件AddItem.js内容如下:

    export default class AddItem extends Component {
      static defaultProps = {
              add: () => {}
      }
      static propTypes = {
          add: React.PropTypes.func.isRequired
      }
      handleSubmit = (e) => {
        if(e.keyCode === 13){
          var newItem = this.myTextInput.value;
          this.myTextInput.value = '';
          this.props.add(newItem);
        }
      }
      render = () => {
        return (
          
    this.myTextInput = ref} className="form-control" placeholder="New Item" onKeyDown={this.handleSubmit} />
    ) } };

    onKeyDown函数处理添加事件的提交,会调用父级组件设置的add方法。

    App.js

    入口文件App.js中引入ListContainer即可:

    class App extends Component {
      render() {
        return (
          

    Welcome

    ); } }

    至此,我们可以通过npm start启动应用程序,然后在浏览器中可以通过访问http://localhost:3000/来查看结果了。

    具体代码请移步Github:
    react-flux-demo/todoApp

    Flux

    Flux并不是一个库或者框架,可以将其简单的理解为一种软件架构方式。在其官方网站上,其原理图如下:

    一下子看上去比较晦涩,特别是在其官方文档中,涉及多个新的名词,如Views, Stores, Dispatcher Actions, Action Types, Action Creators如此等等,一下子使其更加难以理解。

    但是,真正熟悉之后,你会发现,其实其涉及的核心概念也就下面这么几个:

    Actions:帮助向Dispatcher传递数据的辅助方法;
    Dispatcher:接收action,并且向注册的回调函数广播payloads;
    Stores:应用程序状态的容器&并且含有注册到Dispatcher的回调函数;
    Views:React组件,从Store获取状态,并将其逐级向下传递给子组件。

    一个重要的特征是,其实现了一个单向的数据流,用于高效的管理state。这就有效的避免了MVC框架中view和model多对多容易出现的各种问题。关于原理的详细介绍,可以参考文后的链接。

    flux架构的实现

    如前所述,Flux是一种架构方式,一种设计思想。因此,Facebook并没有规定其必须如何实现,这也导致其实现版本层出不穷,具体可以参考:

    github.com/kriasoft/re…

    其中最为流行的当属Redux,以后再专文详述。
    本文使用Facebook提供的flux版本

    使用flux架构重构Todo程序

    我们将上面实现的React应用简单做些修改,以便理解flux架构的基本概念及设计思想。

    目录结构

    我们将创建独立的目录来保存stores,actions,dispatcher等。目录结构如下:

    src
    -- actions
    -- constants
    -- dispatcher
    -- stores
    -- components
    

    其中constants用于保存一些常量,components就是我们的React组件,对应flux架构中的views

    Constants

    constants/appConstants.js内容很简单,创建了两个我们需要在action中用到的常量。代码如下:

    var appConstants = {
      ADD_ITEM: "ADD_ITEM",
      REMOVE_ITEM: "REMOVE_ITEM"
    }
    export default appConstants

    Stores

    Stores用于集中管理state的变化。stores/todoStore.js文件内容如下:

    import AppDispatcher from '../dispatcher/AppDispatcher';
    import appConstants from '../constants/appConstants';
    import {EventEmitter} from 'events';
    var CHANGE_EVENT = 'change';
    var _store = {
      list: []
    };
    var addItem = function(item){
      _store.list.push(item);
    };
    var removeItem = function(index){
      _store.list.splice(index, 1);
    }
    var todoStore = Object.assign({}, EventEmitter.prototype, {
      addChangeListener: function(cb){
        this.on(CHANGE_EVENT, cb);
      },
      removeChangeListener: function(cb){
        this.removeListener(CHANGE_EVENT, cb);
      },
      getList: function(){
        return _store.list;
      },
    });

    其中引入events事件机制用于处理storeview之间的事件响应。

    Dispatcher

    Dispatcher用于创建一种事件注册分发的机制,可以简单理解为pub/sub模式(严格意义上并不完全是)。dispatcher/AppDispatcher.js代码如下:

    import { Dispatcher } from 'flux';
    const AppDispatcher = new Dispatcher();
    AppDispatcher.handleAction = function(action){
      this.dispatch({
        source: 'VIEW_ACTION',
        action: action
      });
    };
    export default AppDispatcher;

    注册dispatchers的操作一般放在stores里。在stores/todoStore.js文件中:

    AppDispatcher.register(function(payload){
      var action = payload.action;
      switch(action.actionType){
        case appConstants.ADD_ITEM:
          addItem(action.data);
          todoStore.emit(CHANGE_EVENT);
          break;
        case appConstants.REMOVE_ITEM:
          removeItem(action.data);
          todoStore.emit(CHANGE_EVENT);
          break;
        default:
          return true;
      }
    });

    actions

    action就是我们所要做的动作,在我们的示例中就是添加todo项目和删除todo项目。下发事件(dispatcher)的操作就定义在actions/todoActions.js里:

    import AppDispatcher from '../dispatcher/AppDispatcher';
    import appConstants from '../constants/appConstants';
    var todoActions = {
      addItem: function(item){
        AppDispatcher.handleAction({
          actionType: appConstants.ADD_ITEM,
          data: item
        });
      },
      removeItem: function(index){
        AppDispatcher.handleAction({
          actionType: appConstants.REMOVE_ITEM,
          data: index
        })
      }
    }
    export default todoActions
    

    components

    components中就是我们之前创建的React组件了,需要做的修改就是将states的管理部分代码移到了stores里。因此,对components/ListContainer.js文件作如下修改:

    import React, { Component } from 'react';
    import AddItem from './AddItem'; 
    import List from './List'; 
    import todoStore from '../stores/todoStore';
    import todoActions from '../actions/todoActions';
    export default class ListContainer extends Component {
      state = {
        list: todoStore.getList()
      }
      componentDidMount = () => {
        todoStore.addChangeListener(this._onChange);
      }
      componentWillUnmount = () => {
        todoStore.removeChangeListener(this._onChange);
      }
      handleAddItem = (newItem) => {
        todoActions.addItem(newItem);
      }
      handleRemoveItem = (index) => {
        todoActions.removeItem(index);
      }
      _onChange = () => {
        this.setState({
          list: todoStore.getList()
        })
      }
      render = () => {
        return (
          

    Todo List

    ) } }

    其中所有state的状态变化均通过stores来处理。可以看到,在这里handleAddItemhandleRemoveItem分别会触发action里定义的动作,然后就会触发在store里注册好的dispatcher的操作,进而完成对state的更改。_onChange事件通过events事件机制处理store和view间数据的同步。

    至此,所有的代码修改完毕,我们可以通过npm start启动应用程序,然后在浏览器中可以通过访问http://localhost:3000/来查看结果了。

    具体代码请移步Github:
    react-flux-demo/todoApp-flux

    参考链接

    flux offical site
    flux documentation
    Flux: An Application Architecture for React
    The Flux Quick Start Guide
    A cartoon guide to Flux
    Flux For Stupid People
    Which Flux implementation should I use?
    Creating A Simple Shopping Cart with React.js and Flux
    ruanyf/extremely-simple-flux-demo
    Flux: Getting Past the Learning Curve
    Getting To Know Flux, the React.js Architecture
    React.js Tutorial Pt 3: Architecting React.js Apps with Flux.

    令狐葱 wechat 欢迎您扫一扫上面的二维码,订阅我的微信公众号! 赏 令狐葱 WeChat Pay

    微信打赏

    令狐葱 Alipay

    支付宝打赏