我正在使用React和moment.js构建新闻Feed。使用.map
我正在渲染带有标题和内容的项目。我想检查一件商品是否在同一年份和另一件商品中发布。如果是这种情况,我想隐藏第二项标题。
目前我的代码呈现了这个:
新闻第一项
新闻第二项
新闻第三项
新闻第四项
由于第一项和第二项共享同一个月和一年,我想这样做:
新闻第一项
新闻第二项
新闻第三项
新闻第四项
Based on this answer我试图找到具有重复类名的节点,但我不在那里:
let monthNodes = [...document.querySelectorAll('.months')];
let months = []
monthNodes.map(month => {
months.push(month.className)
})
const count = names =>
names.reduce((a, b) =>
Object.assign(a, {[b]: (a[b] || 0) + 1}), {})
const duplicates = dict =>
Object.keys(dict).filter((a) => dict[a] > 1)
console.log(count(months)) // {March_2018: 2, December_2017: 1, November_2017: 1}
console.log(duplicates(count(months))) // [ 'March_2018' ]
也许我会以错误的方式解决这个问题并且首先使用.map
是一个坏主意?
更新
感谢所有出色的答案,很难在他们之间挑选,因为他们都运作良好。自从他第一次提供一个有效的例子以来,我必须接受David Ibl的回答。
答案 0 :(得分:2)
Array.prototype.reduce()
在Articles
和Object
之后以year
格式整理month
。
请参阅下面的实例。
// News
class News extends React.Component {
// State.
state = {
news: [
{content: 'news item one -- march 2018', date: '2018-03-02'},
{content: 'news item two -- march 2018', date: '2018-03-17'},
{content: 'news item two -- sep 2017', date: '2017-09-21'},
{content: 'news item one -- june 2017', date: '2017-06-15'}
]
}
// Render.
render = () => (
<div>
{
Object.entries(this.organised()).map(([yearmonth, articles], i) => (
<div key={i}>
<h3>{yearmonth}</h3>
{articles.map((article, j) => (
<div key={j}>
{article.content}
</div>
))}
</div>
))
}
</div>
)
// Organised.
organised = () => this.state.news.reduce((total, article) => {
const key = moment(article.date).format('YYYY MMMM')
return {
...total,
[key]: total[key] && total[key].concat(article) || [article]
}
}, {})
}
// Mount.
ReactDOM.render(<News/>,document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.21.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
答案 1 :(得分:1)
render() {
const arr = new Array();
return(
<div>
{this.state.news.map(item => {
const yearMonth = moment(item.date).format("MMMM YYYY");
const yearM = moment(item.date).format("MMMM_YYYY");
if (arr.indexOf(yearMonth) < 0){
arr.push(yearMonth);
return (
<div className={'months ' + yearM}>
<h2>{yearMonth}</h2>
<p>{item.content}</p>
</div>
)
} else {
return (
<div className={'months ' + yearM}>
<p>{item.content}</p>
</div>
)
}
})
}
</div>
)
}
也许您应该根据年份和月份对项目进行排序,以确保即使最初未对项目进行排序也能保证正确的行为。但这适用于你的例子。
答案 2 :(得分:1)
假设数据结构如下所示:
const data = [
{
date: 'March 2018',
item: {
title: 'news item one'
}
},
{
date: 'March 2018',
item: {
title: 'news item two'
}
},
{
date: 'September 2017',
item: {
title: 'news item two'
}
},
{
date: 'June 2017',
item: {
title: 'news item one'
}
}
]
您可以使用https://codepen.io/hulvire/pen/KoVrZb:
const formatted = data.reduce((obj, item) => {
obj[item.date] = obj[item.date] ? [...obj[item.date], item] : [item]
return obj
}, {})
答案 3 :(得分:1)
1)您可以定义previousYearMonth
变量并在那里存储上一年和月份名称。您可以检查当前的yearMonth
和previousYearMonth
是否相同并显示/隐藏相应的标记:
render() {
return(
<div>
{this.state.news.map((item, index) => {
const yearMonth = moment(item.date).format("MMMM YYYY");
const yearM = moment(item.date).format("MMMM_YYYY");
const previousYearMonth = index
? moment(this.state.news[index - 1].date).format("MMMM YYYY")
: false;
return(
<div className={'months ' + yearM}>
{
(yearMonth !== previousYearMonth) && (<h2>{yearMonth}</h2>)
}
<p>{item.content}</p>
</div>
)
})
}
</div>
)
}
2)您可以使用constructor
方法预处理数据并以这种方式设置title
属性的另一种方法:
constructor(props) {
super(props);
const data = [
{content: 'news item one', date: '2018-03-02'},
{content: 'news item two', date: '2018-03-17'},
{content: 'news item two', date: '2017-09-21'},
{content: 'news item one', date: '2017-06-15'}
];
data.forEach((item, index) => {
const yearMonth = moment(item.date).format("MMMM YYYY");
const previousYearMonth = index
? moment(data[index - 1].date).format("MMMM YYYY")
: false;
if (yearMonth !== previousYearMonth) {
item.title = yearMonth
}
});
this.state = {
news: data
}
}
在这种情况下,在render
方法中,您只需检查title
属性是否存在并显示标题:
<div>
{
item.title && (<h2>{item.title}</h2>)
}
<p>{item.content}</p>
</div>
答案 4 :(得分:1)
考虑到此列表来自API,一旦数据到达,您就可以拥有类似的内容。
const monthwise = this.state.news.reduce((res, obj) => {
const ym = moment(obj.date).format("YYYY-MM")
res[ym] = res[ym] || []
res[ym].push(obj);
return res;
}, {})
this.setState({monthwise})
现在,在您的render
中,您可以拥有:
render() {
return(
<div>
{Object.values(this.state.monthwise).map((items, index) => {
return(
<div key={index}>
<h4>{moment(items[0].date).format("YYYY MMMM")}</h4>
{items.map((item, key) => <div key={key}>{item.content}</div> )}
</div>
)
})}
</div>
)
}