所以我试图找出构建特定类型的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都有多个复制,这两个都不好。在AngularJS中,每个组件都可以拥有自己的范围,并从其父组件继承,从而防止出现此问题。
我目前的解决方案是我创建了以下mixin:
var ContentAreaCollapsableMixin = {
getInitialState: function() {
return {
collapsed: false
};
},
toggleCollapse: function() {
this.setState({
collapsed: !this.state.collapsed
});
}
}
现在每个页面元素有多个ContentAreas,我创建了一个自定义的ContentArea元素来满足页面的需求:
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来附加我的事件,因为据我所知我不能将事件附加到自定义/子元素,并且标题不应该总是有这个事件所以我不想污染header指令使用该内容(也许我可能想将该事件添加到标题中的图标而不是整个标题中。)
在处理包装器并具有这样的层次结构的元素时,这是构建此类功能的正确方法吗?
答案 0 :(得分: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>
]
})
在ContentArea中,你可以简单地渲染这些道具,因为你可以渲染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中以应用click处理程序和className respectivley。