2015-04-23 106 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跟蹤活動項目適用於我。 –