【问题标题】:Passing list of values as filtertable content将值列表作为可过滤内容传递
【发布时间】:2016-08-26 07:16:58
【问题描述】:

我正在尝试使用带有 React 的值列表进行过滤。

我所有的“标签”都有一个“标签级别”来表明它们的相关性。

我希望它“取消”相同的标签(即如果标签相同,则不要重复标签)。

  • 我希望第一行显示所有“taglevel”为 1 的 tag.name。

  • 我希望第二行显示所有“taglevel”为 2 或更多的 tag.name。

我无法显示和过滤值“标签”。可能是在我的代码笔的第 145 行附近我犯了错误。

这是我想要实现的目标:

我已经把它放在一个codepen中。

http://codepen.io/yarnball/pen/GqbyWr?editors=1010

没有成功,我现在尝试了以下方法:

我尝试使用以下方法进行过滤:

var LevelFilter = React.createClass({
    render: function(){
        if (!this.props.tags) return null;
        return this.props.tags.filter(tag => tag.taglevel === this.props.targetLevel).map(tag => <a onClick={this.props.onClick}>{tag.name}</a>);
    }
});

然后尝试在我的回报中得到它:

    render: function(){
        ...
        var getUniqueCategories=[];
                PHOTODATA.forEach(function(el){
        if(getUniqueCategories.indexOf(el.tag) === -1 ) getUniqueCategories.push(el.tag);
    })

return (
            <div className="overlay-photogallery">
                <div className="filter-panel"><b>Tags with taglevel 1 only to be displayed</b>
                    {
                        getUniqueCategories.map(function(el,i){
                            var boundClick = titleToSelect.bind(null,el);
                            return <LevelFilter onClick={boundClick} targetLevel={1} tags={el.tag} />
                        })

                    }  
                    <a className="resetBtn" onClick={this.resetFilter}> Reset Filter </a>
                </div>

我的数据如下所示:

    "title": "Into the Wild",
    "tag": [
        {
            "name": "Movie",
            "taglevel": 1,
            "id": 1
        },
        {
            "name": "Adventure",
            "taglevel": 2,
            "id": 30
        },
        {
            "name": "Book",
            "taglevel": 1,
            "id": 2
        }
    ],
    "info": []
}

【问题讨论】:

  • 格式不错的问题,您的详细信息可以帮助我找到解决方案

标签: javascript arrays reactjs tags


【解决方案1】:

TL;DR

您的数组操作和 React 组件存在一些严重问题。

请记住,React 提倡一种特定的自上而下的结构,您应该多阅读一些内容。每个 React 组件都应该尽可能多地使用 props,理想情况下只有 1 个顶级组件应该保持状态。


快速前进:

  1. 向下传递所有数据,让每个级别的过滤器使列表独一无二。
  2. 说真的,拆分组件并让它们尽可能依赖于 props。
  3. 为变量赋予有意义的名称。 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'));

【讨论】:

  • 这似乎是一个更好的方法。谢谢你。我更好地理解它。它的功能有一个问题- filtet/标签应该“堆叠”在彼此之上。 IE 如果我从 1 级和 2 级选择一个过滤器,它只会显示与两个标签相关的项目。这样做需要什么?
  • 这里要看的关键点是selectTagtagFilter 函数。目前,selectTag 在可以追加 (this.setState({displayedCategories: this.state.displayedCategories.concat([tag])})) 时始终替换 displayedCategories,而tagFilter 在可以执行 every 时执行 some 操作i>,但这可能不太正确。我会更新答案。
  • 优秀。谢谢你。快到了!我希望它在没有选择标签时显示所有标签。但是,当单击标签时,我希望它隐藏“标签”,其中不存在以下层的结果。这是如何实现的?
  • 我创建了这个问题以尝试启用过滤器的切换 (stackoverflow.com/questions/39204823)
【解决方案2】:

使用下面的反应组件,我能够做你想要的, 这就是我在代码中所做的,

i) 从我创建的PHOTODATA 数组taglevel1taglevel2 数组 一开始的渲染方法。

ii) 在showLevel1showLevel2 函数中以两行显示它们。

iii) 当标签项被点击时,它会调用handleClick 函数并过滤数据并将其保存到filterData 状态。

import React, { Component } from 'react';
import { pluck }    from 'underscore';

class Router extends Component {
  constructor(props) {
    super(props);
    this.state = {
    filterData: [],
  };
  this.filterArray = [];
    this.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": []
}];
}
handleClick(item) {
const findItem = this.filterArray.indexOf(item);
if (findItem === -1) {
    this.filterArray.push(item);
} else {
  this.filterArray.pop(item);
}
const filterData = [];
if(this.filterArray.length) {
    this.PHOTODATA.map((item) => {
      const data = pluck(item.tag, 'name');
      let count = 0;
      // console.log(data);
      this.filterArray.map((filterItem) => {
        const find = data.indexOf(filterItem);
        if(find !== -1) {
          count++;
        }
        });
        if(count === this.filterArray.length) {
            filterData.push(item);
        }
    });
 }
 console.log(this.filterArray);
 this.setState({ filterData });
}
render() {
const taglevel1 = [];
const taglevel2 = [];
this.PHOTODATA.map((item) => {
        item.tag.map((tagItem) => {
            if(tagItem.taglevel === 1) {
                const find = taglevel1.indexOf(tagItem.name);
                if(find === -1) {
                    taglevel1.push(tagItem.name);
                }
            } else {
                const find = taglevel2.indexOf(tagItem.name);
                if(find === -1) {
                    taglevel2.push(tagItem.name);
                }
            }
        });
});
const showLevel1 = (item, index) => {
  return <span onClick={this.handleClick.bind(this, item)}> {item} </span>
};
const showLevel2 = (item, index) => {
  return <span onClick={this.handleClick.bind(this, item)}> {item} </span>
};
const showData = (item, index) => {
    return <div>{item.title}</div>
};
return (<div>
  <ul>Tag Level 1: {taglevel1.map(showLevel1)}</ul>
  <ul>Tag Level 2: {taglevel2.map(showLevel2)}</ul>
  <div>Movie Title: {this.state.filterData.map(showData)}</div>
</div>);
}}

在这里你可以看到我的输出是什么样子的

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 2013-12-25
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多