Javascript ReactJS和非分层组件

Javascript ReactJS和非分层组件,javascript,contextmenu,reactjs,Javascript,Contextmenu,Reactjs,我一直在研究ReactJS中的上下文菜单模块,它让我思考如何处理非层次化组件 我遇到的问题是,应用程序中的许多不同项可能需要使用上下文菜单。通常在React中,您会将父对象的回调传递给需要与父对象通信的子对象。例如,我的第一个想法是让一个openContextMenumousePosition,optionsObject函数从我的ContextMenu类传递给所有希望在右键单击时显示上下文菜单的元素 但对于所有这些元素,甚至任何元素都是上下文菜单的子元素,这是没有意义的!相对于应用程序的其他组件

我一直在研究ReactJS中的上下文菜单模块,它让我思考如何处理非层次化组件

我遇到的问题是,应用程序中的许多不同项可能需要使用上下文菜单。通常在React中,您会将父对象的回调传递给需要与父对象通信的子对象。例如,我的第一个想法是让一个openContextMenumousePosition,optionsObject函数从我的ContextMenu类传递给所有希望在右键单击时显示上下文菜单的元素

但对于所有这些元素,甚至任何元素都是上下文菜单的子元素,这是没有意义的!相对于应用程序的其他组件,上下文菜单不是分层的。在Angular中,我可能会编写一个ContextMenu服务,如果组件想要访问这样一个菜单,它们就需要该服务


这是应该使用全局事件处理程序的情况吗?我是不是完全错了?如何处理组件之间的这种水平交互?

上下文菜单非常特殊。任何时候都不应打开多个上下文菜单。它们也很特别,因为它可以从任何地方打开。试着弄清楚当把它们放在一起时是什么样子的

为了解决我们的全局问题,我们将创建一个mixin,其中封装一个私有事件发射器

var menuEvents = new events.EventEmitter();
var ContextMenuMixin = {
  // this.openContextMenu(['foo', 'bar'], (err, choice) => void)
  openContextMenu: function(options, callback){
    menuEvents.emit('open', {
      options: options, 
      callback: callback
    });
  },
  closeContextMenu: function(){
    menuEvents.emit('close');
  }
};
现在对于组件,我们需要做一些事情。这里是初始化部分。只是绑定到一些事件,以及轻量级鼠标跟踪

var mouse = {x: 0, y: 0};
var updateMouse = function(e){
  mouse.x = e.pageX;
  mouse.y = e.pageY;
};

var ContextMenu = React.createClass({
  getInitialState: function(){
    return {options: null, callback: null};
  },
  componentDidMount: function(){
    menuEvents.addListener('open', this.handleOpenEvent);
    menuEvents.addListener('close', this.closeMenu);
    addEventListener('mousemove', updateMouse);
  },
这些事件处理程序非常简单。HandLeopBenEvent只将事件有效负载和鼠标位置存储在状态中,这将有效地锁定鼠标位置,直到下一次打开它为止。而对应方只是重置状态,并调用带有错误的回调

 handleOpenEvent: function(payload){
    this.setState(_.merge({}, payload, mouse));
  },

  closeMenu: function(){
    if (this.state.callback) {
      this.replaceState(this.getInitialState());
      this.state.callback(new Error('no selection made'));
    }
  },
最后,我们呈现传递给事件的选项列表,并为每个选项创建单击处理程序

render: function(){
    if (!this.state.options) {
      return <div />
    }

    var style = {
      left: this.state.x,
      top: this.state.y,
      position: 'fixed'
    };

    return (
      <div className="react-contextmenu" style={style}>
        <ul className="react-contextmenu-options">
          {this.state.options.map(function(x, i){
            return <li key={i} 
                       onClick={this.makeClickHandler(x)}>
                    {x}
                   </li>
          }, this)}
        </ul>
      </div>
    );
  },

  makeClickHandler: function(option){
    return function(){
      if (this.state.callback) {
        this.state.callback(null, option);
        this.replaceState(this.getInitialState());
      }
    }.bind(this);
  }

上下文菜单很特别。任何时候都不应打开多个上下文菜单。它们也很特别,因为它可以从任何地方打开。试着弄清楚当把它们放在一起时是什么样子的

为了解决我们的全局问题,我们将创建一个mixin,其中封装一个私有事件发射器

var menuEvents = new events.EventEmitter();
var ContextMenuMixin = {
  // this.openContextMenu(['foo', 'bar'], (err, choice) => void)
  openContextMenu: function(options, callback){
    menuEvents.emit('open', {
      options: options, 
      callback: callback
    });
  },
  closeContextMenu: function(){
    menuEvents.emit('close');
  }
};
现在对于组件,我们需要做一些事情。这里是初始化部分。只是绑定到一些事件,以及轻量级鼠标跟踪

var mouse = {x: 0, y: 0};
var updateMouse = function(e){
  mouse.x = e.pageX;
  mouse.y = e.pageY;
};

var ContextMenu = React.createClass({
  getInitialState: function(){
    return {options: null, callback: null};
  },
  componentDidMount: function(){
    menuEvents.addListener('open', this.handleOpenEvent);
    menuEvents.addListener('close', this.closeMenu);
    addEventListener('mousemove', updateMouse);
  },
这些事件处理程序非常简单。HandLeopBenEvent只将事件有效负载和鼠标位置存储在状态中,这将有效地锁定鼠标位置,直到下一次打开它为止。而对应方只是重置状态,并调用带有错误的回调

 handleOpenEvent: function(payload){
    this.setState(_.merge({}, payload, mouse));
  },

  closeMenu: function(){
    if (this.state.callback) {
      this.replaceState(this.getInitialState());
      this.state.callback(new Error('no selection made'));
    }
  },
最后,我们呈现传递给事件的选项列表,并为每个选项创建单击处理程序

render: function(){
    if (!this.state.options) {
      return <div />
    }

    var style = {
      left: this.state.x,
      top: this.state.y,
      position: 'fixed'
    };

    return (
      <div className="react-contextmenu" style={style}>
        <ul className="react-contextmenu-options">
          {this.state.options.map(function(x, i){
            return <li key={i} 
                       onClick={this.makeClickHandler(x)}>
                    {x}
                   </li>
          }, this)}
        </ul>
      </div>
    );
  },

  makeClickHandler: function(option){
    return function(){
      if (this.state.callback) {
        this.state.callback(null, option);
        this.replaceState(this.getInitialState());
      }
    }.bind(this);
  }

你能提供你的代码让我们更好地了解你的困境吗?你能提供你的代码让我们更好地了解你的困境吗?非常酷!这是一个版本的演示,它有一个有效的EventEmitter库和下划线,因此可以在预览窗格中工作。嗯,看起来那个垃圾箱没被救出来。我看到控制台中没有定义EventEmitter2。我对React不太熟悉,但这是否泄漏了从未解除绑定的事件侦听器?可能会,但ContextMenu旨在永不解除绑定。jsbin中的示例显示它是主组件的最后一个直接子组件,它应该位于或安装到主体末尾的自己的节点上。我应该清理它们以确保正确性,但除非您做了其他非常错误的事情,否则它不会显示出来。var EventEmitter2=require'EventEmitter2'。EventEmitter2;var menuEvents=neweventemitter2;很酷!这是一个版本的演示,它有一个有效的EventEmitter库和下划线,因此可以在预览窗格中工作。嗯,看起来那个垃圾箱没被救出来。我看到控制台中没有定义EventEmitter2。我对React不太熟悉,但这是否泄漏了从未解除绑定的事件侦听器?可能会,但ContextMenu旨在永不解除绑定。jsbin中的示例显示它是主组件的最后一个直接子组件,它应该位于或安装到主体末尾的自己的节点上。我应该清理它们以确保正确性,但除非您做了其他非常错误的事情,否则它不会显示出来。var EventEmitter2=require'EventEmitter2'。EventEmitter2;var menuEvents=neweventemitter2;