《深入浅出React与Redux》读书笔记2 ——FLUX

    在学习Redux之前,了解下Flux的思想是非常必要的。Flux是和React同时面世的,在2013年,Face-book公司让React亮相的同时,也推出了Flux框架,React和Flux相辅相成,Facebook认为两者结合在一起才能构建大型的JavaScript应用。做一个容易理解的对比,React是用来替换jQuery的,那么Flux就是以替换Backbone.js、Ember.js等MVC一族框架为目的。

Flux框架结构大致如下:

《深入浅出React与Redux》读书笔记2 ——FLUX_第1张图片
Flux框架

一个Flux应用包含四个部分,我们先粗略了解一下:
1、Dispatcher,处理动作分发,维持Store之间的依赖关系;
2、Store,负责存储数据和处理数据相关逻辑;
3、Action,驱动Dispatcher的JavaScript对象;
4、View,视图部分,负责显示用户界面。

Flux应用

下面我们来做一个flux demo,实现加减和求和;
为了使用Flux,首先通过命令行在项目目录下安装Flux。
npm install –-save flux

1、Dispatcher
首先,我们要创造一个Dispatcher,几乎所有应用都只需要拥有一个Dispatcher,在src/AppDispatcher.js中,我们创造这个唯一的Dispatcher对象,代码如下:

import {Dispatcher} from 'flux';

export default new Dispatcher();

Dispatcher存在的作用,就是用来派发action,接下来我们就来定义应用中涉及的action。

2、Action
action对象必须有一个名为type的字段,代表这个action对象的类型,为了记录日志和debug方便,这个type应该是字符串类型。定义action通常需要两个文件,一个定义action的类型,一个定义action的构造函数(也称为action creator)。分成两个文件的主要原因是在Store中会根据action类型做不同操作,也就有单独导入action类型的需要。在src/ActionTypes.js中,我们定义action的类型,代码如下:

export const INCREMENT = 'increment';

export const DECREMENT = 'decrement';

在这个例子中,用户只能做两个动作,一个是点击“+”按钮,一个是点击“-”按钮,所以我们只有两个action类型INCREMENT和DECREMENT。现在我们在src/Actions.js文件中定义action构造函数:

import * as ActionTypes from './ActionTypes.js';
import AppDispatcher from './AppDispatcher.js';

export const increment = (counterCaption) => {
  AppDispatcher.dispatch({
    type: ActionTypes.INCREMENT,
    counterCaption: counterCaption
  });
};

export const decrement = (counterCaption) => {
  AppDispatcher.dispatch({
    type: ActionTypes.DECREMENT,
    counterCaption: counterCaption
  });
};

在Actions.js文件中,引入了ActionTypes和AppDispatcher,看得出来是要直接使用Dispatcher。这个Actions.js导出了两个action构造函数increment和decrement,当这两个函数被调用的时候,创造了对应的action对象,并立即通过AppDispatcher.dispatch函数派发出去。

3.Store
一个Store也是一个对象,这个对象存储应用状态,同时还要接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态。现在,我们创造两个Store,一个是负责加减的CounterStore,另一个就是为总数服务的SummaryStore。
我们首先添加CounterStore,放在src/stores/CounterStore.js文件中。
先看定义CounterStore的代码,如下所示:

import AppDispatcher from '../AppDispatcher.js';
import * as ActionTypes from '../ActionTypes.js';
import {EventEmitter} from 'events';

const CHANGE_EVENT = 'changed';

const counterValues = {
  'First': 10,
  'Second': 20,
  'Third': 30
};


const CounterStore = Object.assign({}, EventEmitter.prototype, {
  getCounterValues: function() {
    return counterValues;
  },

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }

});
export default CounterStore;

上面实现的Store只有注册到Dispatcher实例上才能真正发挥作用,所以还需要增加下列代码:

CounterStore.dispatchToken = AppDispatcher.register((action) => {
  if (action.type === ActionTypes.INCREMENT) {
    counterValues[action.counterCaption] ++;
    CounterStore.emitChange();
  } else if (action.type === ActionTypes.DECREMENT) {
    counterValues[action.counterCaption] --;
    CounterStore.emitChange();
  }
});

接下来,我们再来看看另一个Store,也就是代表所有计数器计数值总和的Store,在src/stores/SummaryStore.js中有源代码。

import AppDispatcher from '../AppDispatcher.js';
import * as ActionTypes from '../ActionTypes.js';
import CounterStore from './CounterStore.js';
import {EventEmitter} from 'events';

const CHANGE_EVENT = 'changed';

function computeSummary(counterValues) {
  let summary = 0;
  for (const key in counterValues) {
    if (counterValues.hasOwnProperty(key)) {
      summary += counterValues[key];
    }
  }
  return summary;
}

const SummaryStore = Object.assign({}, EventEmitter.prototype, {
  getSummary: function() {
    return computeSummary(CounterStore.getCounterValues());
  },

  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }

});


SummaryStore.dispatchToken = AppDispatcher.register((action) => {
  if ((action.type === ActionTypes.INCREMENT) ||
      (action.type === ActionTypes.DECREMENT)) {
    AppDispatcher.waitFor([CounterStore.dispatchToken]);

    SummaryStore.emitChange();
  }
});

export default SummaryStore;

4、View
存在于Flux框架中的React组件需要实现以下几个功能:
1.创建时要读取Store上状态来初始化组件内部状态;
2.当Store上状态发生变化时,组件要立刻同步更新内部状态保持一致;
3.View如果要改变Store状态时,必须而且只能派发action;

View层我们拆分三个组件,ControlPanel、Control、Summary;

先看src/views/ControlPanel.js,代码如下:

import React, { Component } from 'react';
import Counter from './Counter.js';
import Summary from './Summary.js';

class ControlPanel extends Component {

  render() {
    return (
      

); } } export default ControlPanel;

看下两个子组件,首先是Counter:

import React, { Component, PropTypes } from 'react';

import * as Actions from '../Actions.js';
import CounterStore from '../stores/CounterStore.js';


class Counter extends Component {

  constructor(props) {
    super(props);

    this.onChange = this.onChange.bind(this);
    this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
    this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

    this.state = {
      count: CounterStore.getCounterValues()[props.caption]
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (nextProps.caption !== this.props.caption) ||
           (nextState.count !== this.state.count);
  }

  componentDidMount() {
    CounterStore.addChangeListener(this.onChange);
  }

  componentWillUnmount() {
    CounterStore.removeChangeListener(this.onChange);
  }

  onChange() {
    const newCount = CounterStore.getCounterValues()[this.props.caption];
    this.setState({count: newCount});
  }

  onClickIncrementButton() {
    Actions.increment(this.props.caption);
  }

  onClickDecrementButton() {
    Actions.decrement(this.props.caption);
  }

  render() {
    const {caption} = this.props;
    return (
      
{caption} count: {this.state.count}
); } } Counter.propTypes = { caption: PropTypes.string.isRequired }; export default Counter;

其次是求和的Summary

import React, { Component } from 'react';

import SummaryStore from '../stores/SummaryStore.js';

class Summary extends Component {

  constructor(props) {
    super(props);

    this.onUpdate = this.onUpdate.bind(this);

    this.state = {
      sum: SummaryStore.getSummary()
    }
  }

  componentDidMount() {
    SummaryStore.addChangeListener(this.onUpdate);
  }

  componentWillUnmount() {
    SummaryStore.removeChangeListener(this.onUpdate);
  }

  onUpdate() {
    this.setState({
      sum: SummaryStore.getSummary()
    })
  }

  render() {
    return (
      
Total Count: {this.state.sum}
); } } export default Summary;

这个简单的加减求和demo就做好了,效果如下:


《深入浅出React与Redux》读书笔记2 ——FLUX_第2张图片
效果图

你可能感兴趣的:(《深入浅出React与Redux》读书笔记2 ——FLUX)