实现一个简单的flux

前言

众所周知,React跟Flux是一对好基友。
其中,市场流行的Flux有Redux,Mobx,Reflux。
其中,用法最简单的是Reflux。
其数据流思路如下:

+---------+       +--------+       +-----------------+
¦ Actions ¦------>¦ Stores ¦------>¦ View Components ¦
+---------+       +--------+       +-----------------+
     ^                                      ¦
     +--------------------------------------+

我们能否再减少其数据流路径?如下:

 +--------+       +-----------------+
 ¦ Stores ¦------>¦ View Components ¦
 +--------+       +-----------------+
     ^                     ¦
     ----------------------+

两个字,可以。

需求分析

  1. 集成Actions的功能到Stores。从而拿掉单独的Actions。
  2. 集成组件的State和Store在一起。
  3. 跨组件通信依赖其Store。

撸函数

这意味着我们的createStore是一个工厂函数。
用来生产每一个React组件实例对应的Store实例。

function isObject(obj) {
  return Object.prototype.toString.call(obj) == '[object Object]';
}

function extend(obj) {
  if (!isObject(obj)) {
    return obj;
  }
  for (let i = 1, length = arguments.length; i < length; i++) {
    let source = arguments[i];

    for (let prop in source) {
      if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
        let propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop);
        Object.defineProperty(obj, prop, propertyDescriptor);
      } else {
        obj[prop] = source[prop];
      }
    }
  }
  return obj;
}

function createStore(definition={}) {

  function Store() {
    let t = this;
    t.data = null;
    extend(t, definition);
    t.trigger = function () {
      this.setState({});
    }

  }
  let store = new Store();
  return store;
};

isObjectextend这两个函数按下不表。
其中,extend函数是用来对象合并,该函数某部位依赖isObject。((__) 嘻嘻……)
createStore函数产出的实例内部

  1. data作为组件实例的store。
  2. trigger作为更新组件实例的方法。

万事具备,只欠东风。

接下来就是用connect函数把组件实例和Store实例连接在一起。

function connect(listenable, context) {
  if(!isObject(listenable)){
    throw new Error('connect function\'s argument is not a object');
  }
  return {
    componentDidMount() {
      context = context || this;
      listenable.trigger = listenable.trigger.bind(context);
    },
    componentWillUnmount() {
      listenable.trigger = null;
    }
  };
}

借用React组件生命周期,在其componentDidMount阶段,
改变Store实例的trigger上下文,使其指向React组件实例,
从而方便trigger调用React组件实例的setState方法。
全套代码如下:

撸Demo

  • Samflux.js
function isObject(obj) {
  return Object.prototype.toString.call(obj) == '[object Object]';
}

function extend(obj) {
  if (!isObject(obj)) {
    return obj;
  }
  for (let i = 1, length = arguments.length; i < length; i++) {
    let source = arguments[i];

    for (let prop in source) {
      if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
        let propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop);
        Object.defineProperty(obj, prop, propertyDescriptor);
      } else {
        obj[prop] = source[prop];
      }
    }
  }
  return obj;
}

exports.createStore = function (definition={}) {

  function Store() {
    let t = this;
    t.data = null;
    extend(t, definition);
    t.trigger = function () {
      this.setState({});
    }

  }
  let store = new Store();
  return store;
};

exports.connect = function (listenable, context) {
  if(!isObject(listenable)){
    throw new Error('connect function\'s argument is not a object');
  }
  return {
    componentDidMount() {
      context = context || this;
      listenable.trigger = listenable.trigger.bind(context);
    },
    componentWillUnmount() {
      listenable.trigger = null;
    }
  };
}

  • store.js
const Samflux = require('./Samflux.js');
const Store = Samflux.createStore({
  data:'old data',
  onSetData: function(){
    this.data = 'new data';
    this.trigger();
  },
});

module.exports = Store;
  • SamfluxTest.js
const Store = require('./store.js');
const Samflux = require('./Samflux.js');
const reactMixin = require('react-mixin');
const React = window.React;
require('./SamfluxTest.less');

class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }
  setData(){
    Store.onSetData();
  }
  render() {
    const me = this;
    return (<div className="SamfluxTest" onClick={me.setData.bind(this)}><span>{Store.data}</span></div>);
  }
}

reactMixin.onClass(Test, Samflux.connect(Store));
module.exports = Test;

posted @ 2017-03-14 23:38  草珊瑚  阅读(424)  评论(0编辑  收藏  举报