2016-03-02 80 views
0

我正在尝试使用MenuItem创建菜单,从头开始。我正在使用React 0.14和Redux,以及CoffeeScript(cjsx)。 (我刚刚开始使用所有这些)React/Redux - 菜单和项目的父项和子项之间的事件

我被卡住了,因为我不知道如何给MenuItem(s)引用回调函数onChildrenClick。因为它们是通过{this.props.children}生成的,我不知道我应该如何在那里添加props

Menu.cjsx

React = require 'react' 

class Menu extends React.Component 

    @propTypes = 
     children: React.PropTypes.array.isRequired 

    render: -> 
     <div className="ui pointing menu"> 
      {this.props.children} # I don't see how I can bind the "onChildrenClick" function to the child, because they're generated outside of this scope. Maybe by looping on "this.props.children"? I tried but it wasn't successful. 
     </div> 

    onChildrenClick: (event) -> 
     console.log event 

module.exports = Menu 

MenuItem.cjsx

React = require 'react' 
classNames = require('classnames') 

class MenuItem extends React.Component 

    @propTypes = 
     label: React.PropTypes.string.isRequired 
     active: React.PropTypes.bool 

    constructor: (props) -> 
     super props 

     @state = 
      classes: classNames(
       'ui' 
       'item' 
       'active': this.props.active 
      ) 

    render: -> 
     <a className={@state.classes} onClick={@onClick}> 
      { this.props.label } 
     </a> 

    onClick: => 
     console.log Object.assign @state.classes, {active: [email protected]} 
     @setState({classes: Object.assign @state.classes, {active: true}}) 
     console.log @state.classes # I need to notify the parent so it can notify the current active children to get disabled. 

module.exports = MenuItem 

下面是我建立我的菜单及其项目:

<Menu> 
    <MenuItem active={true} label="Menu 1"></MenuItem> 
    <MenuItem label="Menu 2"></MenuItem> 
    <MenuItem label="Menu 3"></MenuItem> 
</Menu> 

在这个例子中,我手动生成“菜单1 ... 3“,但它们实际上是动态生成的。

我不知道我是否在这里得到了正确的方法。也许我应该使用Redux来做这件事?最后,我应该使用Routes,因为每个菜单项都应该有自己的网址(角等,与#menu-1,例如)

我也想知道这将是给一个菜单项内容的最佳方法。感谢您的见解。

回答

1

这可能是React.Children候选人,因此你可以指定props传递到任何abritrary子元素 - 在这种情况下onClick

Menu.jsx

<div className="ui pointing menu"> 
{ 
     React.Children.map(this.props.children, 
      function(child) { 
       return React.cloneElement(child, { 
        onClick: this.onClick 
       }); 
      }.bind(this) 
    ) 
} 
</div> 

但我可能会建议一种替代方法,即将列表项目传递给Menu组件本身,然后它可以单独渲染每个列表项目,鼠标她不仅仅是为this.props.children

Menu.jsx(ALT)

render() { 
    return (
     <div className="ui pointing menu"> 
      { 
      this.props.menuItems.map((item, index) => (
       <MenuItem item={item} onClick={(e) => this.onClick(e, item)} }/> 
      ) 
      } 
     </div> 
    ) 
} 
+0

我曾考虑过其他选项,但我对内容并不确定。因为每个MenuItem应该以某种方式与要显示的内容相关。或者它可能是一个链接...在这种情况下,改变uri路径。 – Vadorequest

+0

@Vadorequest啊,我明白了 - 也许把这件物品作为一个“小孩”来传递就够了?在这种情况下,它更像是一个'MenuItem'包装:' this.onClick(e,item)}}> {item}' – lux

0

我建议传递onClick功能的MenuItem无论你正在渲染Menu道具。

E.g.

menu_wrapper.js

// This is a functional component because it is 'dumb' - does not have state 

React = require('react') 
Menu = require('menu') 
MenuItem = require('menu_item') 

menuWrapper = (props)=> 
    onMenuItemClick = (e)=> 
    console.log(e) 

    menuItems =()=> 
    props.list.map(function(item) { 
     isActive = (item.label == props.activeItem) 
     <MenuItem onClick={onMenuItemClick} active={isActive} label={item.label}/> 
    } 

    <div id="menu-wrapper"> 
    <Menu> 
     {menuItems()} 
    </Menu> 
    </div> 

menu.js

// Also a 'dumb' component - component without state 

React = require('react') 

Menu = (props)=> 
    <div className="ui pointing menu"> 
    {props.children} 
    </div> 

module.exports = Menu 

MENU_ITEM。js

React = require 'react' 
classNames = require('classnames') 

class MenuItem extends React.Component 
    @propTypes = 
     label: React.PropTypes.string.isRequired 
     active: React.PropTypes.bool 
     onClick: React.PropTypes.func.isRequired 

    constructor: (props) -> 
     super props 

     @state = 
      classes: classNames(
       'ui' 
       'item' 
       'active': this.props.active 
     ) 

    render: -> 
     <a className={@state.classes} onClick={@onClick}> 
      { this.props.label } 
     </a> 

    onClick: (e)=> 
     @props.onClick(e) 
     console.log Object.assign @state.classes, {active: [email protected]} 
     @setState({classes: Object.assign @state.classes, {active: true}}) 
     console.log @state.classes # I need to notify the parent so it can notify the current active children to get disabled. 

module.exports = MenuItem 

最后,考虑从CoffeeScript转移到ES6。您在CoffeeScript中获得的大部分内容现在都可以在ES6中找到,还有一些。这就是说,我确实想念隐性回报。

+0

我不喜欢这种方法,因为你菜单的逻辑超出其范围。消费者不提供菜单的逻辑,而是菜单本身,否则它不是正确的组件模式。 – Vadorequest

+0

有效。您记录事件的例子可以通过MenuItem本身的onClick来完成。如果你真的想要从'Menu'传入的onClick,那么为什么不使用事件委派? – rguerrettaz