2016-03-02 73 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