从外部访问React状态

时间:2014-09-26 13:40:13

标签: architecture reactjs

我想使用React.js创建一个应用程序。我希望它可以从外部世界轻松定制(例如,通过编写用户脚本)。我试图使用的想法是在根元素状态中创建一些特殊属性(如sidebarItemsplaylistCreatedHooks),因此插件开发人员可以在那里添加一些内容。我的问题是:这是一个很好的方法,是否有正确的方法来实现类似于我的目标,最后,插件开发者将如何访问这些道具?

2 个答案:

答案 0 :(得分:8)

一种选择是可观察的。基本上它是一个对象,你可以听取变化,并创建一个变化。您还可以发出其他事件,例如添加' data.playlists上的事件,用于创建您想要提供的API。

// data.js
var data = {
  sidebarItems: Observable([]),
  playlists: Observable([])
};

// app.js
var App = React.createComponent({
  mixins: [data.sidebarItems.mixin("sidebar")],
  render: function(){
    return this.state.sidebar.map(renderSidebarItem);
  }
});

/// userscript.js

// causes the view to update
data.sidebarItems.set(somethingElse); 

// run when someone does data.playlists.set(...)
data.playlists.on('change', function(playlists){

});

// an event you could choose to emit with data.playlists.emit('add', newPlaylist)
data.playlists.on('add', function(newPlaylist){

});

这是上面使用的Observable的一个示例(未经测试的)实现,带有用于生成反应组件mixin的额外函数。

var events = require('events'); // or some other way of getting it
var Observable = function(initialValue){
  var self = new events.EventEmitter();
  var value = initialValue;

  self.get = function(){ return value };
  self.set = function(updated){
    value = updated;
    self.emit('change', updated);
  };
  self.mixin = function(key){
    var cbName = Math.random().toString();
    var mixin = {
      getInitialState: function(){ var o = {}; o[key] = value; return o },
      componentDidMount: function(){
        self.on('change', this[cbName]);
      },
      componentWillUnmount: function(){
        self.removeListener('change', this[cbName]);
      }
    }
    mixin[cbName] = function(){
      var o = {}; o[key] = value; this.setState(o);
    };
    return mixin;
  }

  return self;
}

答案 1 :(得分:1)

这是我的解决方案。感谢这个Observable React组件'状态会自动更新(及其后果,如重新呈现组件),您甚至可以通过.on方法监听外部的更改。

var eventEmitter = {
    _JQInit: function() {
        this._JQ = jQuery(this);
    },
    emit: function(evt, data) {
        !this._JQ && this._JQInit();
        this._JQ.trigger(evt, data);
    },
    once: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.one(evt, handler);
    },
    on: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.bind(evt, handler);
    },
    off: function(evt, handler) {
        !this._JQ && this._JQInit();
        this._JQ.unbind(evt, handler);
    }
};

var Observable = function(initialValue, name) {
    var self = eventEmitter;
    var name = name;
    var obj = {
        value: initialValue,
        ops: self
    };

    self.get = function() {
        return obj.value
    };

    self.set = function(updated){
        if(obj.value == updated)
            return;

        obj.value = updated;
        self.emit('change', updated);
    };

    self.mixin = function() {
        var mixin = {
            getInitialState: function() {
                var obj_ret = {};
                obj_ret[name] = obj;

                return obj_ret;
            },
            componentDidMount : function() {
                self.on('change', function() {
                    var obj_new = {};
                    obj_new[name] = obj;

                    this.setState(obj_new);
                }.bind(this));
            }
        };

        return mixin;
    };

    return self;
};

示例(使用它来显示另一个组件的警报):     // Observable init     alert_msg =可观察('',' alertmsg');

var ConfirmBtn = React.createClass({
    mixins: [alert_msg.mixin()],
    handleConfirm: function(e) {
        e.preventDefault();

        this.state.alertmsg.ops.set(null);

        if(! $('#cgv').is(':checked')) {
            this.state.alertmsg.ops.set('Please accept our terms of conditions');
            return;
        }
    }
}

var AlertPayment = React.createClass({
    mixins: [alert_msg.mixin()],

    render: function() {
        var style = (this.state.alertmsg === null) ? {display: 'none'} : {display: 'block'};

        return (
            <div style={style} className="alertAcceptTerms">
                {this.state.alertmsg.value}
            </div>
        );
    }
});

希望有所帮助