2014-12-06 75 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