2015-04-23 116 views
0

我正在学习反应,并希望实现一个基本的导航栏中有一些项目,我想使最后一个我点击有className活动和所有其他没有。从反应更新兄弟对象

在下面的代码中,我无法删除resetALL与item.setState中的className(是通过阅读文档我明白它不起作用),所以我试图直接使用jQuery操纵dom。它在第一次点击时有效,但第二次点击没有效果。有没有任何反应原生的方式来做到这一点?谢谢。

$(document).ready(function(){ 

     var NavItem = React.createClass({ 
      getInitialState: function() { 
       return this.props.item; 
      }, 

      handleClick: function() { 
       React.render(<span>{this.props.item.description}</span>, document.getElementById('main')); 

       this.props.resetALL(); 
       console.log("this", this); 
       this.setState({active: true}); 
      }, 

      render: function() { 
       var className = this.state.active ? "active" : ""; 
       return <li className={className}><a href="#" onClick={this.handleClick}>{this.props.item.description}</a></li>; 
      } 
     }); 

     var NavBar = React.createClass({ 
      items : [], 

      resetALL: function() { 
       this.items.forEach(function(item) { 
        console.log("item", item); 
        // item.setState({active: false}); 
       }); 

       $("#nav-sidebar li").each(function(){ 
        $(this).removeClass("active"); 
       }); 
      }, 

      render: function() { 
       var _this = this; 

       this.props.items.forEach(function(item) { 
        console.log("handleClick", this.handleClick); 
        _this.items.push(<NavItem item={item} key={item.id} resetALL={_this.resetALL}/>); 
       }); 

       return (
        <ul className="nav nav-sidebar" id="nav-sidebar"> 
         {this.items} 
        </ul> 
       ); 
      } 

     }); 


     var NAVLIST = [ 
      {id: 1, description: 'OverView', active: true}, 
      {id: 2, description: 'Calls'}, 
      {id: 3, description: 'Channels'}, 
      {id: 4, description: 'OverView'} 
     ] 

     React.render(<NavBar items = {NAVLIST} />, document.getElementById('sidebar')); 


     React.render(
      <h1>Hello, FreeSWITCH!</h1>, 
      document.getElementById('main') 
     ); 

    }); 

回答

0

NavItem电话this.props.resetALL(),功能没有被绑定到NavBar,所以它无法通过NavBar的项目迭代。你可以把它绑定到正确的范围是这样的:

<NavItem item={item} key={item.id} resetALL={_this.resetALL.bind(this)}/> 
0

好了,我看到你的代码中的一些东西,有点少见反应:

  • 重新渲染每次main元素事件发生有点击败react.js的目的。相反,你应该通过属性和事件沟通,让react更新你main想要的文字
  • NavItem不应该负责制定有效状态,NavBar是一个更合适的地方,因为它拥有其他项目,它可以很容易地激活/停用需要一个
  • 我建议你保存items阵列为state,而不是一个成员变量,因为任何时候你要激活其他项目您只需更新状态
  • 力推_this.items.push反应的组分仅仅是反应方式,你不应该直接存储和访问组件
  • 通过渲染反应会干扰直接操作DOM元素反应
  • 还你不需要$(document).ready()

下面你可以找到一个react.js自然的方法,您的问题。三件事情需要注意:
1.数据出现故障,事件上去
2. setState是用来让反应过来知道,事情发生了转变
3.有一个聚合App组件,可帮助您更新文本

var NavBar = React.createClass({ 
    itemClicked: function(ev) { 
     var items = this.state.items; 
     var clickedId = ev.currentTarget.getAttribute("data-item-id"); 
     var activeItem = {}; 
     items.forEach(function(item) { 
      if(item.id == clickedId) { 
       activeItem = item; 
      } 
      item.active = item.id == clickedId; 
     }); 
     //let react do the work 
     this.setState({items:items}); 
     if(this.props.onItemChange) { 
      this.props.onItemChange(activeItem); 
     } 
    }, 
    getInitialState: function() { 
     return {items: this.props.items}; 
    }, 
    render: function() { 
     var navItems = this.state.items.map(function(item, idx) { 
      var className = item.active ? "active" : ""; 
      return <li key={item.id} className={className}><a href="#" data-item-id={item.id} onClick={this.itemClicked}>{item.description}</a></li>; 
     }.bind(this)); 

     return (
      <ul className="nav nav-sidebar" id="nav-sidebar"> 
       {navItems} 
      </ul> 
     ); 
    } 

}); 

var App = React.createClass({ 
    handleItemChange: function(item) { 
     this.setState({selectedItem: item}); 
    }, 
    getInitialState: function() { 
     return {selectedItem: {description:"Hello, FreeSWITCH!"}}; 
    }, 
    render: function() { 
     return (<div> 
      <h1>{this.state.selectedItem.description}</h1> 
      <div id="sidebar"> 
       <NavBar items={this.props.items} onItemChange={this.handleItemChange} /> 
      </div> 
     </div>); 
    } 
}); 


var NAVLIST = [ 
    {id: 1, description: 'OverView', active: true}, 
    {id: 2, description: 'Calls'}, 
    {id: 3, description: 'Channels'}, 
    {id: 4, description: 'OverView'} 
] 

React.render(<App items = {NAVLIST} />, document.getElementById('content')); 
+0

谢谢,现在我明白了。 ottonomy的解决方案现在适用于我。我最终将使用一个应用程序来追踪我所想的一切。 –

+0

嗯,你不需要跟踪所有的东西,但它会很好:) – Cristik

+0

嗯,我的意思是它可以跟踪一切,如果我想。正如你所提到的,我每次点击一个Item时都会重新呈现主要元素,这就像切换页面一样。它的作用虽然不是反应的目的。但是Navbar和main元素是两个不同的元素,我必须使用window.addEventListener的子PubSub来让它们相互交谈,正如http://maketea.co.uk/2014/03/05/building-robust- web-apps-with-react-part-1.html。如果在App中包装所有东西,我们可以使用一些父母/孩子的回调,但是当孩子树变得太深时,我害怕吗?这是最佳做法? –

0

对于使用jQuery来更新由React管理的DOM元素的类,你是对的。每当React重新呈现时,您的jQuery更改通常会被覆盖。要很好地使用React,你必须做很多思考和重新思考你想要用你的应用程序来表示什么,以及定义它的状态的数据应该自然存在的地方。

在此示例中,您正尝试呈现其中只有其中一个导航链接处于活动状态的导航链接列表。 NavBar是存储哪个项目比NavItem的状态更有效的地方。

我建议通过在项目列表当道具NavList,并存储NavList的状态开始活动项目或将它作为一个明确标示initiallyActiveItem这样的:

<NavList items={NAVLIST} initialActiveItem={1} /> 

不过的标识活动项目应存放在NavList的状态,而不是道具,这样做的这么一种方式是使用NavList的getInitialState生命周期方法来初始化活动项目:

getInitialState: function() { return { activeItem: this.props.initialActiveItem }; }, 

当您需要更新活动项目,像点击处理r,你可以使用setState调用,比如this.setState({activeItem: 2}

我想在NavList上定义一个函数,它需要一个id并调用它的setState方法来将活动项设置为该id,然后将该函数作为prop传递给每个项目(在此行this是的NavBar实例,itemthis.props.items一个元素):

<NavItem id={item.id} update={this.updateActive} key={item.id} active={this.state.activeId == item.id} /> 

NavItem只需要知道它的props.active是否以决定是否呈现活跃的className是真实的,以及如何将它的id传递回NavBar的updateActive函数,以便使其自身处于活动状态(如果它尚未启用)。当NavBar更新时,它将重新呈现其子NavItems,并且它们将被更新为正确的活动状态,以便它们可以呈现自己正确的类名。

当然,如果单击NavItems的目的不仅仅是更新是否该项目处于活动状态,您必须确保任何其他相关操作都将由该点击触发。如果这意味着更新完全不同的React组件的内容(例如,已经呈现到#main中的内容),请考虑将在哪里存储和检索有关应该在其他组件中的状态信息。它可能应该是这两个元素的父项,或者像Flux Store一样的其他共享有状态资源。请参阅https://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html了解更多关于如何推断此信息所在的位置的信息。

+0

谢谢,使用activeItem跟踪活动项目适用于我。 –