2014-12-06 76 views
1

所以我想找出結構特定類型的ReactJS元素的最佳方式。構建ReactJS組件,其中兄弟組件需要通信

因此,讓我們說我有這個元素稱爲ContentArea。 ContentArea可以由許多其他自定義元素組成,ContentAreaHeader,ContentAreaContent和ContentAreaAction。 ContentArea,ContentAreaHeader和ContentAreaContent基本上都是包裝器元素,它將子元素用適當的類包裝在正確的HTML元素中。 ContentAreaAction的實現對這個問題並不重要,只是想提到它來顯示有很多不同的元素。 ContentArea應該只有1個標題元素,但應該能夠支持多個其他項目(ContentAreaContent和/或ContentAreaAction)。

一個功能是能夠點擊標題並切換顯示標題旁邊的其他元素。從AngularJS世界的到來,我最初雖然是創造一個指令,我可以重複使用,所以我嘗試在ReactJS和我的代碼看了這本:

var MyPage = React.createClass({ 
    render: function() { 
    return (
     <ContentArea> 
     <ContentAreaHeader>My Header</ContentAreaHeader> 
     <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent> 
     </ContentArea> 
    ); 
    } 
}); 

現在,我可以添加事件和摺疊狀態的東西這個MyPage組件,但之後我只能爲每個頁面元素創建1個ContentArea,或者爲每個ContentArea複製多個ContentArea,這兩者都不是很好。在AngularJS中,每個組件都可以有自己的作用域並從它的父代繼承,這可以防止這個問題。

我目前的解決方案是,我創建了以下的mixin:

var ContentAreaCollapsableMixin = { 
    getInitialState: function() { 
    return { 
     collapsed: false 
    }; 
    }, 

    toggleCollapse: function() { 
    this.setState({ 
     collapsed: !this.state.collapsed 
    }); 
    } 
} 

我們能夠讓每個頁面元素多ContentAreas,我創建一個自定義的含量 - 面積元素的網頁的需求:

var MyContentArea = React.createClass({ 
    mixins: [ 
    contentArea.mixins.collapsable 
    ], 

    render: function() { 
    var cssClasses = []; 

    console.log(this.state.collapsed); 

    if(this.state.collapsed) { 
     cssClasses.push('u-hide'); 
    } 

    return (
     <ContentArea> 
     <span onClick={this.toggleCollapse}><ContentAreaHeader>My Header</ContentAreaHeader></span> 
     <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent> 
     </ContentArea> 
    ); 
    } 
}); 

var MyContentArea2 = React.createClass({ 
    mixins: [ 
    contentArea.mixins.collapsable 
    ], 

    render: function() { 
    var cssClasses = []; 

    if(this.state.collapsed) { 
     cssClasses.push('u-hide'); 
    } 

    return (
     <ContentArea> 
     <span onClick={this.toggleCollapse}><ContentAreaHeader>My Header</ContentAreaHeader></span> 
     <ContentAreaContent className={cssClasses.join(' ')}>My Content</ContentAreaContent> 
     <ContentAreaContent className={cssClasses.join(' ')}>My Content2</ContentAreaContent> 
     </ContentArea> 
    ); 
    } 
}); 

var ContentAreaComponents = React.createClass({ 
    render: function() { 
    return (
     <div> 
     <h1 id="test" className="test">Content Area</h1> 
     <MyContentArea /> 
     <MyContentArea2 /> 
     </div> 
    ); 
    } 
}); 

注意我使用span來附加我的事件,因爲據我所知我不能將事件附加到自定義/子元素,並且標題不應該總是有這個事件,所以我不想污染標題指令與該內容(也許我可能需要將該事件添加到標題中的圖標而不是整個標題)。

這是在處理包裝器的元素並且具有這樣的層次結構時構建此類功能的正確方法嗎?

+0

「我不能將事件附加到自定義/子元素」默認情況下這可能是真的,但是如果你的ContentAreaHeader實現類似於'return

...
;',那麼事件處理程序將通過' ContentAreaHeader onClick = {this.toggleCollapse}>'你不需要包裝'span'。 – icktoofay 2014-12-06 21:40:11

+0

@icktoofay我明白,然而,頭的內容並不固定,我可能並不總是希望在主頭元素上的點擊事件(就像我提到的,也許有時候我希望它在頭元素中的svg元素上)。 – ryanzec 2014-12-07 10:22:24

+0

那麼你就會將'onClick'傳播給不是元素本身的東西。例如。 'ContentAreaHeader'的實現將具有'

+ ...
'。 – icktoofay 2014-12-07 19:22:23

回答

1

最簡單的方法是將組件作爲道具傳遞。例如:

<ContentArea 
    header={"My Header"} 
    content={[ 
    <div>My Content</div>, 
    <div>My Other Content</div> 
    ]} 
    /> 

這看起來JSX有點奇怪,所以你可以不用,如果你喜歡。

React.createElement(ContentArea, { 
    header: "My Header", 
    content: [ 
    <div>My Content</div>, 
    <div>My Other Content</div> 
    ] 
}) 

在含量 - 面積,你可以簡單地使這些道具爲你呈現props.children,但有更多的控制。

var ContentArea = React.createClass({ 
    getInitialState: function(){ return {open: true} }, 
    toggleOpen: function(){ this.setState({open: !this.state.open}) }, 

    render: function(){ 
    var className = this.state.open ? "" : "hidden"; 

    return (
     <div> 
     <ContentAreaHeader onClick={this.toggleOpen}> 
      {this.props.header} 
     </ContentAreaHeader> 

     {this.props.content.map(function(element, index){ 

      return (
      <ContentAreaContent className={className} key={index}> 
       {element} 
      </ContentAreaContent> 
     ); 

     })} 
     </div> 
    ); 
    } 
}); 

在這個例子中得到的結構是:

<ContentArea> 
    <div> 
    <ContentAreaHeader>My Header</ContentAreaHeader> 
    <ContentAreaContent className="..." key="0"> 
     <div>My Content</div> 
    </ContentAreaContent> 

    <ContentAreaContent className="..." key="1"> 
     <div>My Other Content</div> 
    </ContentAreaContent> 
    </div> 
</ContentArea> 

這是不破壞任何規則的方式。使用你提到的API的方法是使用React.Children.map並根據索引確定它是否爲標題或內容(例如,0是標題,1..infinity是內容),然後將其包裝在div中以應用點擊處理程序和className。

+0

2件事情,您的解決方案不允許我確定內容是ContentAreaContent還是ContentAreaAction(可能有內容類型),2.您提到「這是不違反任何規則的方式」,但是不要提到我打破了什麼規則,我對我的工具破壞了什麼規則感興趣。 – ryanzec 2014-12-07 10:32:34

+0

首先你可以修改它以適合你的需求,但我並不完全明白你在做什麼。對於破壞規則,我的意思是試圖查看props.children中的內容,並檢查它們是什麼組件或其他未記錄的黑客事件。 – FakeRainBrigand 2014-12-07 16:53:20