TL;DR
您的数组操作和 React 组件存在一些严重问题。
请记住,React 提倡一种特定的自上而下的结构,您应该多阅读一些内容。每个 React 组件都应该尽可能多地使用 props,理想情况下只有 1 个顶级组件应该保持状态。
快速前进:
- 向下传递所有数据,让每个级别的过滤器使列表独一无二。
- 说真的,拆分组件并让它们尽可能依赖于 props。
- 为变量赋予有意义的名称。
el 没有意义,在您的情况下是指 PHOTODATA 数组中的 PHOTO 项,在 PHOTO 中标记,然后您使用 element 再次表示其他含义。不要太过分,但至少能够确定变量应该做什么。
我已经放弃并制作了一个结构更新很多的代码笔。行为可能并不完全符合您的要求,但请查看代码及其组织方式以及信息在组件之间的共享和传递方式。
http://codepen.io/anon/pen/AXGGLy?editors=1010
更新
要允许多个过滤器,应更新两个方法:
selectTag: function (tag) {
this.setState({
displayedCategories: this.state.displayedCategories.concat([tag])
});
}
tagFilter: function (photo) {
return this.props.displayedCategories.length !== 0 &&
this.props.displayedCategories.every(function(thisTag) {
return photo.tag.some(function (photoTag) {
return photoTag.id === thisTag.id &&
photoTag.taglevel === thisTag.taglevel;
});
});
},
selectTag 现在追加到显示的类别数组而不是替换它。
tagFilter 现在检查是否已应用至少一个过滤器(删除 this.props.displayedCategories.length !== 0 以禁用此功能),以便默认情况下不显示所有过滤器,然后检查 每个 选择的过滤器是否存在于每个photo 中,从而使组件具有附加性。
可以进行进一步的改进,例如在该级别应用过滤器时禁用 级别(每个级别有一个选择)或显示已应用过滤器的列表,无论是通过按钮上的颜色或结果上方的标签列表。
(codepen 更新了这些最新更改)
好的,你的 codepen 有一些问题。
首先,在第 137 行,您从对象中提取标签数组:
if(getUniqueCategories.indexOf(el.tag) === -1 ) getUniqueCategories.push(el.tag);
然后,在 146 上再次提取它:
return <LevelFilter onClick={boundClick} targetLevel={1} tags={el.tag} />
第 2 级:
return <LevelFilter onClick={boundClick} targetLevel={2} tags={el.tag} />
对于这两个应该是:
return <LevelFilter onClick={boundClick} targetLevel={n} tags={el} />
这会导致另一个问题出现,即 LevelFilter 没有返回有效的 React 组件(数组无效)。
return this.props.tags.filter(tag => tag.taglevel === this.props.targetLevel).map(tag => <a onClick={this.props.onClick}>{tag.name}</a>);
应该是
return (
<div>
{
this.props.tags
.filter(tag => tag.taglevel === this.props.targetLevel)
.map(tag => <a onClick={this.props.onClick}>{tag.name}</a>)
}
</div>
);
在这些更改之后,您应该更接近您想要的位置。
还有其他问题需要调查,比如 boundClick 函数将无法正常工作,因为您只有标签列表,而不是 PHOTODATA。
然而,只是最后的想法。你可能想进一步分解你的 React 组件。
作为参考,这里是来自 codepen 的完整代码清单:
var PHOTODATA = [{
"title": "Into the Wild",
"tag": [
{
"name": "Movie",
"taglevel": 1,
"id": 1
},
{
"name": "Adventure",
"taglevel": 2,
"id": 30
},
{
"name": "Book",
"taglevel": 1,
"id": 2
}
],
"info": []
},{
"title": "Karate Kid",
"tag": [
{
"name": "Movie",
"taglevel": 1,
"id": 1
},
{
"name": "Adventure",
"taglevel": 2,
"id": 30
},
{
"name": "Kids",
"taglevel": 3,
"id": 4
}
],
"info": []
},
{
"title": "The Alchemist",
"tag": [
{
"name": "Book",
"taglevel": 1,
"id": 2
},
{
"name": "Adventure",
"taglevel": 2,
"id": 30
},
{
"name": "Classic",
"taglevel": 2,
"id": 4
},
{
"name": "Words",
"taglevel": 4,
"id": 4
}
],
"info": []
}];
var PhotoGallery = React.createClass({
getInitialState: function() {
return {
displayedCategories: []
};
},
selectTag: function (tag) {
this.setState({
displayedCategories: this.state.displayedCategories.concat([tag])
});
},
resetFilter: function(){
this.setState({
displayedCategories: []
});
},
render: function(){
var uniqueCategories = PHOTODATA.map(function (photo) {
return photo.tag; // tag is a list of tags...
}).reduce(function (uniqueList, someTags) {
return uniqueList.concat(
someTags.filter(function (thisTag) {
return !uniqueList.some(function(uniqueTag) {
return uniqueTag.id === thisTag.id && uniqueTag.taglevel === thisTag.taglevel
});
})
);
}, []);
return (
<div className="overlay-photogallery">
<div className="filter-panel"><b>Tags with taglevel 1 only to be displayed</b>
<PhotoGalleryLevel level={1} tags={uniqueCategories} displayedCategories={this.state.displayedCategories} selectTag={this.selectTag} />
<a className="resetBtn" onClick={this.resetFilter}> Reset Filter </a>
</div>
<div className="filter-panel"><b>Tags with taglevel 2 only to be displayed</b>
<PhotoGalleryLevel level={2} tags={uniqueCategories} displayedCategories={this.state.displayedCategories} selectTag={this.selectTag} />
</div>
<div className="PhotoGallery">
<PhotoDisplay displayedCategories={this.state.displayedCategories} photoData={PHOTODATA} />
</div>
</div>
);
}
});
var PhotoGalleryLevel = React.createClass({
render: function () {
var filteredTags = this.props.tags.filter(function (tag) {
return tag.taglevel === this.props.level;
}.bind(this));
var disabled = this.props.displayedCategories.some(function (tag) {
return tag.taglevel === this.props.level;
}.bind(this));
return (
<div>
{filteredTags.map(function (tag){
return <PhotoGalleryButton tag={tag} selectTag={this.props.selectTag} disabled={disabled} />;
}.bind(this))}
</div>
);
}
});
var PhotoGalleryButton = React.createClass({
onClick: function (e) {
this.props.selectTag(this.props.tag);
},
render: function () {
return (
<a className={this.props.disabled} onClick={this.onClick}>{this.props.tag.name}</a>
);
}
});
var PhotoDisplay = React.createClass({
getPhotoDetails: function (photo) {
console.log(this.props.displayedCategories, photo);
return (
<Photo title={photo.title} name={photo.name} tags={photo.tag} />
);
},
tagFilter: function (photo) {
return this.props.displayedCategories.length !== 0 &&
this.props.displayedCategories.every(function(thisTag) {
return photo.tag.some(function (photoTag) {
return photoTag.id === thisTag.id &&
photoTag.taglevel === thisTag.taglevel;
});
});
},
render: function () {
return (
<div>
{this.props.photoData.filter(this.tagFilter).map(this.getPhotoDetails)}
</div>
);
}
});
var Photo = React.createClass({
getTagDetail: function (tag){
return (
<li>{tag.name} ({tag.taglevel})</li>
);
},
sortTags: function (tagA, tagB) {
return tagA.taglevel - tagB.taglevel;
},
render: function(){
return (
<div className="photo-container" data-title={this.props.title} >
{this.props.title}
<ul>
{this.props.tags.sort(this.sortTags).map(this.getTagDetail)}
</ul>
</div>
);
}
});
ReactDOM.render(<PhotoGallery />, document.getElementById('main'));