用于无限滚动的 React JS 地图数组
React JS map array for infinite scroll
我有一个简单的 React 应用程序,我在其中获取 Flickr Public Feed API 并显示它。不幸的是,它多次映射阵列,我可以在其中看到重复的照片。请求总是 returns 一个包含 20 个具有相同图片的项目的数组,解释重复。
检查下面的代码:
import React, { Component } from 'react';
import $ from 'jquery';
import PhotoListItem from '../../components/photoListItem';
import Searchbar from '../../components/searchBar';
import ScrollButton from '../../components/scrollButton';
import '../app/index.css';
export default class PhotoApp extends Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
searchTerm: 'cyanotype',
items: 10,
loadingState: false,
}
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint = "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" + this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: 'jsonp',
data: { format: "json" },
success: function (data) {
this.setState({ photoList: data.items });
}.bind(this)
});
}
catch (err) {
console.log(err);
}
}
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20) {
this.loadMoreItems();
}
});
}
/* */
displayItems = () => {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(
this.state.photoList.map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1]
const description = photo.description.split(/"/)[13]
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags} />
)
})
);
}
return items;
}
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (
<div className='appContainer' ref="iScroll">
<div className='appHeader'>
<h1 className='headerTitle'>Welcome to Flickr Alternative Photography Feed!</h1>
</div>
<div className='gridContainer'>
{this.displayItems()}
</div>
{this.state.loadingState ? <p className='loading'>Loading items...</p> : ""}
</div>
);
}
}
现场直播EXAMPLE
问题出在 this.displayItems()
,但我该如何解决这个问题?
任何帮助表示赞赏。谢谢!
您可以根据要在 JSX 中显示的项目数量对数组进行切片来实现此目的:
this.state.photoList.slice(0, this.state.items).map(
然后您将不得不使用 setState
的回调版本来使用您状态的旧值并增加您想要显示的内容:
this.setState(old => ({ items: old.items + 2, loadingState: false }));
完整功能示例(建议使用 "full page" 选项):
class PhotoListItem extends React.Component {
render() {
return (
<div className="image-card">
<img className="image-card__image" alt="" src={this.props.url} />
<div className="image-card__body">
<div className="image-title">
<a href={this.props.photoLink}>{this.props.title}</a>
<span className="image-author">
{" "}
by <a href={this.props.authorLink}>{this.props.author}</a>
</span>
</div>
<div className="image-description">
<span className="description">Description:</span>{" "}
{this.props.description}
</div>
<div className="image-tags">
<span className="tags">Tags:</span> {this.props.tags}
</div>
</div>
</div>
);
}
}
class PhotoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
items: 2,
searchTerm: "cyanotype",
loadingState: false
};
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint =
"https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: "jsonp",
data: { format: "json" },
success: function(data) {
this.setState({ photoList: data.items });
}.bind(this)
});
} catch (err) {
console.log(err);
}
};
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight - 20
) {
this.loadMoreItems();
}
});
};
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState(old => ({ items: old.items + 2, loadingState: false }));
}, 1000);
this.getPhotoList();
};
render() {
return (
<div className="appContainer" ref="iScroll">
<div className="appHeader">
<h1 className="headerTitle">
Welcome to Flickr Alternative Photography Feed!
</h1>
</div>
<div className="gridContainer">
{this.state.photoList.slice(0, this.state.items).map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1];
const description = photo.description.split(/"/)[13];
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags}
/>
);
})}
</div>
{this.state.loadingState ? (
<p className="loading">Loading items...</p>
) : (
""
)}
</div>
);
}
}
ReactDOM.render(<PhotoApp />, document.getElementById("root"));
body,
html {
margin: 0;
min-height: 100%;
}
.appContainer {
font-family: "Helvetica", sans-serif;
width: 100%;
height: 100vh;
overflow: auto;
}
.appHeader {
text-align: center;
background-color: #033666;
padding: 1rem;
}
.headerTitle {
color: #fff;
}
.gridContainer {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
padding: 1rem;
grid-gap: 1rem 1rem;
}
.loading {
text-align: center;
color: #033666;
}
@media only screen and (max-width: 320px) {
.appHeader>h1 {
font-size: 1.2rem;
}
}
a,
a:visited {
color: #000;
text-decoration: none;
}
a:hover {
color: #033666;
text-decoration: underline;
}
.image-card {
display: flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
flex-direction: column;
width: auto;
height: auto;
margin: .5rem;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, .15);
background: #fff;
}
.image-card__image {
border-radius: 5px 5px 0 0;
width: 100%;
height: 200px;
object-fit: cover;
}
.image-card__body {
padding: .5rem 1rem 1rem;
}
.image-title {
font-weight: 600;
margin: 0;
word-wrap: break-word;
padding-bottom: .7rem;
cursor: pointer;
}
.image-author {
font-weight: 100;
font-size: .8rem;
cursor: pointer;
}
.image-owner {
margin-top: 0;
font-size: .8rem;
}
.image-date-view-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.image-description {
padding-bottom: .7rem;
font-size: .9rem;
word-wrap: break-word;
}
.tags,
.description {
font-weight: 600;
}
.image-tags {
font-size: .8rem;
word-wrap: break-word;
}
.App {
font-family: sans-serif;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root" />
我有一个简单的 React 应用程序,我在其中获取 Flickr Public Feed API 并显示它。不幸的是,它多次映射阵列,我可以在其中看到重复的照片。请求总是 returns 一个包含 20 个具有相同图片的项目的数组,解释重复。
检查下面的代码:
import React, { Component } from 'react';
import $ from 'jquery';
import PhotoListItem from '../../components/photoListItem';
import Searchbar from '../../components/searchBar';
import ScrollButton from '../../components/scrollButton';
import '../app/index.css';
export default class PhotoApp extends Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
searchTerm: 'cyanotype',
items: 10,
loadingState: false,
}
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint = "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" + this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: 'jsonp',
data: { format: "json" },
success: function (data) {
this.setState({ photoList: data.items });
}.bind(this)
});
}
catch (err) {
console.log(err);
}
}
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20) {
this.loadMoreItems();
}
});
}
/* */
displayItems = () => {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(
this.state.photoList.map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1]
const description = photo.description.split(/"/)[13]
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags} />
)
})
);
}
return items;
}
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (
<div className='appContainer' ref="iScroll">
<div className='appHeader'>
<h1 className='headerTitle'>Welcome to Flickr Alternative Photography Feed!</h1>
</div>
<div className='gridContainer'>
{this.displayItems()}
</div>
{this.state.loadingState ? <p className='loading'>Loading items...</p> : ""}
</div>
);
}
}
现场直播EXAMPLE
问题出在 this.displayItems()
,但我该如何解决这个问题?
任何帮助表示赞赏。谢谢!
您可以根据要在 JSX 中显示的项目数量对数组进行切片来实现此目的:
this.state.photoList.slice(0, this.state.items).map(
然后您将不得不使用 setState
的回调版本来使用您状态的旧值并增加您想要显示的内容:
this.setState(old => ({ items: old.items + 2, loadingState: false }));
完整功能示例(建议使用 "full page" 选项):
class PhotoListItem extends React.Component {
render() {
return (
<div className="image-card">
<img className="image-card__image" alt="" src={this.props.url} />
<div className="image-card__body">
<div className="image-title">
<a href={this.props.photoLink}>{this.props.title}</a>
<span className="image-author">
{" "}
by <a href={this.props.authorLink}>{this.props.author}</a>
</span>
</div>
<div className="image-description">
<span className="description">Description:</span>{" "}
{this.props.description}
</div>
<div className="image-tags">
<span className="tags">Tags:</span> {this.props.tags}
</div>
</div>
</div>
);
}
}
class PhotoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
items: 2,
searchTerm: "cyanotype",
loadingState: false
};
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint =
"https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: "jsonp",
data: { format: "json" },
success: function(data) {
this.setState({ photoList: data.items });
}.bind(this)
});
} catch (err) {
console.log(err);
}
};
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight - 20
) {
this.loadMoreItems();
}
});
};
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState(old => ({ items: old.items + 2, loadingState: false }));
}, 1000);
this.getPhotoList();
};
render() {
return (
<div className="appContainer" ref="iScroll">
<div className="appHeader">
<h1 className="headerTitle">
Welcome to Flickr Alternative Photography Feed!
</h1>
</div>
<div className="gridContainer">
{this.state.photoList.slice(0, this.state.items).map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1];
const description = photo.description.split(/"/)[13];
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags}
/>
);
})}
</div>
{this.state.loadingState ? (
<p className="loading">Loading items...</p>
) : (
""
)}
</div>
);
}
}
ReactDOM.render(<PhotoApp />, document.getElementById("root"));
body,
html {
margin: 0;
min-height: 100%;
}
.appContainer {
font-family: "Helvetica", sans-serif;
width: 100%;
height: 100vh;
overflow: auto;
}
.appHeader {
text-align: center;
background-color: #033666;
padding: 1rem;
}
.headerTitle {
color: #fff;
}
.gridContainer {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
padding: 1rem;
grid-gap: 1rem 1rem;
}
.loading {
text-align: center;
color: #033666;
}
@media only screen and (max-width: 320px) {
.appHeader>h1 {
font-size: 1.2rem;
}
}
a,
a:visited {
color: #000;
text-decoration: none;
}
a:hover {
color: #033666;
text-decoration: underline;
}
.image-card {
display: flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
flex-direction: column;
width: auto;
height: auto;
margin: .5rem;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, .15);
background: #fff;
}
.image-card__image {
border-radius: 5px 5px 0 0;
width: 100%;
height: 200px;
object-fit: cover;
}
.image-card__body {
padding: .5rem 1rem 1rem;
}
.image-title {
font-weight: 600;
margin: 0;
word-wrap: break-word;
padding-bottom: .7rem;
cursor: pointer;
}
.image-author {
font-weight: 100;
font-size: .8rem;
cursor: pointer;
}
.image-owner {
margin-top: 0;
font-size: .8rem;
}
.image-date-view-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.image-description {
padding-bottom: .7rem;
font-size: .9rem;
word-wrap: break-word;
}
.tags,
.description {
font-weight: 600;
}
.image-tags {
font-size: .8rem;
word-wrap: break-word;
}
.App {
font-family: sans-serif;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root" />