使用 react-infinite-scroller 创建待办事项列表
Creating a todos list using react-infinite-scroller
正在尝试使用 react-infinite-scroller
创建待办事项列表。该列表将显示 20 个待办事项,滚动时将显示下一个待办事项。我从“https://jsonplaceholder.typicode.com/todos”获取待办事项。获取的待办事项保存在 todos
变量中。
我已经模拟了这个例子:https://github.com/CassetteRocks/react-infinite-scroller/blob/master/docs/src/index.js。
此处演示:https://cassetterocks.github.io/react-infinite-scroller/demo/.
我看不到 Loading..
出现和获取更多任务。
代码在这里:https://stackblitz.com/edit/react-tcm9o2?file=index.js
import InfiniteScroll from 'react-infinite-scroller';
import qwest from 'qwest';
class App extends Component {
constructor (props) {
super(props);
this.state = {
todos: [],
hasMoreTodos: true,
nextHref: null
}
}
loadTodos(page){
var self = this;
const url = 'https://jsonplaceholder.typicode.com/todos';
if(this.state.nextHref) {
url = this.state.nextHref;
}
qwest.get(url, {
linked_partitioning: 1,
page_size: 10
}, {
cache: true
})
.then((xhr, resp) => {
if(resp) {
var todos = self.state.todos;
resp.map((todo) => {
todos.push(todo);
});
if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});
} else {
self.setState({
hasMoreTodos: false
});
}
}
});
}
render () {
const loader = <div className="loader">Loading ...</div>;
console.log(this.state.todos);
var items = [];
this.state.todos.map((todo, i) => {
items.push(
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>
);
});
return (
<InfiniteScroll
pageStart={0}
loadMore={this.loadTodos.bind(this)}
hasMore={this.state.hasMoreTodos}
loader={loader}>
<div className="tracks">
{items}
</div>
</InfiniteScroll>
)
}
}
更新了我的问题
如何在代码中设置注释的这些地方?
/*if(this.state.nextHref) {
url = this.state.nextHref;
}*/
and
/*if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});*/
如何设置页面从 1 变为 2,下一个从 2 变为 3?
class App extends Component {
constructor (props) {
super(props);
this.state = {
todos: [],
hasMoreTodos: true,
page: 1
}
}
loadTodos(page){
var self = this;
const url = `/api/v1/project/tasks?expand=todos&filter%5Btype%5D=400&${page}&${per_page}`;
/*if(this.state.nextHref) {
url = this.state.nextHref;
}*/
axios.get(url, {
'page': 1,
'per-page': 10
})
.then(resp => {
if(resp) {
var todos = [self.state.todos, ...resp];
/*if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});*/
} else {
self.setState({
hasMoreTodos: false
});
}
}
});
}
render () {
const loader = <div className="loader">Loading ...</div>;
var divStyle = {
'width': '100px';
'height': '300px';
'border': '1px solid black',
'scroll': 'auto'
};
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);
return (
<div style={divStyle}>
<InfiniteScroll
pageStart={1}
loadMore={this.loadTodos.bind(this)}
hasMore={this.state.hasMoreTodos}
loader={loader}>
<div className="tracks">
{items}
</div>
</InfiniteScroll>
</div>
)
}
}
已更新 2
代码在这里:https://stackblitz.com/edit/react-4lwucm
import InfiniteScroll from 'react-infinite-scroller';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
hasMoreTodos: true,
page: 1,
nextPage: 1,
finishedLoading: false
};
}
loadTodos = (id) => {
if(!this.state.finishedLoading) {
const params = {
id: '1234',
page: this.state.nextPage,
'per-page': 10,
}
axios({
url: `/api/v1/project/tasks`,
method: "GET",
params
})
.then(res => {
let {todos, nextPage} = this.state;
if(resp) { //resp or resp.data ????
this.setState({
todos: [...todos, ...resp], //resp or resp.data ????
nextPage: nextPage + 1,
finishedLoading: resp.length > 0 //resp or resp.data ????
});
}
})
.catch(error => {
console.log(error);
})
}
}
render() {
const loader = <div className="loader">Loading ...</div>;
const divStyle = {
'height': '300px',
'overflow': 'auto',
'width': '200px',
'border': '1px solid black'
}
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);
return (
<div style={divStyle} ref={(ref) => this.scrollParentRef = ref}>
<div>
<InfiniteScroll
pageStart={0}
loadMore={this.loadTodos}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={() => this.scrollParentRef}
>
{items}
</InfiniteScroll>
</div>
</div>
);
}
}
编辑: 我刚刚意识到你使用 this 作为起点:
为什么你一次得到所有东西
好吧,因为你的来源只是一个大JSON。
您使用的脚本中的逻辑适用于 SoundCloud 的 API,它对巨大的列表进行分页(又名 returns 逐块)。
您的 API 不分页,因此您一次收到所有内容。
查看来自 SoundCloud 的示例结果 API:
{
"collection": [
{
"kind": "track",
"id": 613085994,
"created_at": "2019/04/29 13:25:27 +0000",
"user_id": 316489116,
"duration": 476281,
[...]
},
[...]
],
"next_href": "https://api.soundcloud.com/users/8665091/favorites?client_id=caf73ef1e709f839664ab82bef40fa96&cursor=1550315585694796&linked_partitioning=1&page_size=10"
}
next_href
中的URL给出了下一批物品的URL(URL相同,但'cursor'参数不同每一次)。这就是使无限列表起作用的原因:每次,它都会获取下一批。
您必须实施服务器端分页才能实现此结果。
回应您的编辑:
UPDATED MY QUESTION
How can I set these places commented in the code?
/*if(this.state.nextHref) {
url = this.state.nextHref; }*/
and
/*if(resp.next_href) { self.setState({
todos: todos,
nextHref: resp.next_href });*/
忘记加载 nextHref,你没有那个。您拥有的是当前页码(您已将内容加载到哪一页)。
尝试以下方法:
if (!self.state.finishedLoading) {
qwest.get("https://your-api.org/json", {
page: self.state.nextPage,
'per-page': 10,
}, {
cache: true
})
.then((xhr, resp) => {
let {todos, nextPage} = self.state;
if(resp) {
self.setState({
todos: [...todos, ...resp],
nextPage: nextPage + 1,
finishedLoading: resp.length > 0, // stop loading when you don't get any more results
});
}
});
}
一些挑剔
除了使用具有副作用的 Array.map
(并且仅针对该副作用)之外,您还改变了 self.state
(bad):
var todos = self.state.todos;
resp.map((todo) => {
todos.push(todo);
});
你可以这样写:
var todos = [self.state.todos, ...resp];
此处地图的用法相同:
var items = [];
this.state.todos.map((todo, i) => {
items.push(
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>
);
});
应该写成:
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);
正在尝试使用 react-infinite-scroller
创建待办事项列表。该列表将显示 20 个待办事项,滚动时将显示下一个待办事项。我从“https://jsonplaceholder.typicode.com/todos”获取待办事项。获取的待办事项保存在 todos
变量中。
我已经模拟了这个例子:https://github.com/CassetteRocks/react-infinite-scroller/blob/master/docs/src/index.js。
此处演示:https://cassetterocks.github.io/react-infinite-scroller/demo/.
我看不到 Loading..
出现和获取更多任务。
代码在这里:https://stackblitz.com/edit/react-tcm9o2?file=index.js
import InfiniteScroll from 'react-infinite-scroller';
import qwest from 'qwest';
class App extends Component {
constructor (props) {
super(props);
this.state = {
todos: [],
hasMoreTodos: true,
nextHref: null
}
}
loadTodos(page){
var self = this;
const url = 'https://jsonplaceholder.typicode.com/todos';
if(this.state.nextHref) {
url = this.state.nextHref;
}
qwest.get(url, {
linked_partitioning: 1,
page_size: 10
}, {
cache: true
})
.then((xhr, resp) => {
if(resp) {
var todos = self.state.todos;
resp.map((todo) => {
todos.push(todo);
});
if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});
} else {
self.setState({
hasMoreTodos: false
});
}
}
});
}
render () {
const loader = <div className="loader">Loading ...</div>;
console.log(this.state.todos);
var items = [];
this.state.todos.map((todo, i) => {
items.push(
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>
);
});
return (
<InfiniteScroll
pageStart={0}
loadMore={this.loadTodos.bind(this)}
hasMore={this.state.hasMoreTodos}
loader={loader}>
<div className="tracks">
{items}
</div>
</InfiniteScroll>
)
}
}
更新了我的问题
如何在代码中设置注释的这些地方?
/*if(this.state.nextHref) {
url = this.state.nextHref;
}*/
and
/*if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});*/
如何设置页面从 1 变为 2,下一个从 2 变为 3?
class App extends Component {
constructor (props) {
super(props);
this.state = {
todos: [],
hasMoreTodos: true,
page: 1
}
}
loadTodos(page){
var self = this;
const url = `/api/v1/project/tasks?expand=todos&filter%5Btype%5D=400&${page}&${per_page}`;
/*if(this.state.nextHref) {
url = this.state.nextHref;
}*/
axios.get(url, {
'page': 1,
'per-page': 10
})
.then(resp => {
if(resp) {
var todos = [self.state.todos, ...resp];
/*if(resp.next_href) {
self.setState({
todos: todos,
nextHref: resp.next_href
});*/
} else {
self.setState({
hasMoreTodos: false
});
}
}
});
}
render () {
const loader = <div className="loader">Loading ...</div>;
var divStyle = {
'width': '100px';
'height': '300px';
'border': '1px solid black',
'scroll': 'auto'
};
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);
return (
<div style={divStyle}>
<InfiniteScroll
pageStart={1}
loadMore={this.loadTodos.bind(this)}
hasMore={this.state.hasMoreTodos}
loader={loader}>
<div className="tracks">
{items}
</div>
</InfiniteScroll>
</div>
)
}
}
已更新 2
代码在这里:https://stackblitz.com/edit/react-4lwucm
import InfiniteScroll from 'react-infinite-scroller';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
hasMoreTodos: true,
page: 1,
nextPage: 1,
finishedLoading: false
};
}
loadTodos = (id) => {
if(!this.state.finishedLoading) {
const params = {
id: '1234',
page: this.state.nextPage,
'per-page': 10,
}
axios({
url: `/api/v1/project/tasks`,
method: "GET",
params
})
.then(res => {
let {todos, nextPage} = this.state;
if(resp) { //resp or resp.data ????
this.setState({
todos: [...todos, ...resp], //resp or resp.data ????
nextPage: nextPage + 1,
finishedLoading: resp.length > 0 //resp or resp.data ????
});
}
})
.catch(error => {
console.log(error);
})
}
}
render() {
const loader = <div className="loader">Loading ...</div>;
const divStyle = {
'height': '300px',
'overflow': 'auto',
'width': '200px',
'border': '1px solid black'
}
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);
return (
<div style={divStyle} ref={(ref) => this.scrollParentRef = ref}>
<div>
<InfiniteScroll
pageStart={0}
loadMore={this.loadTodos}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={() => this.scrollParentRef}
>
{items}
</InfiniteScroll>
</div>
</div>
);
}
}
编辑: 我刚刚意识到你使用 this 作为起点:
为什么你一次得到所有东西
好吧,因为你的来源只是一个大JSON。
您使用的脚本中的逻辑适用于 SoundCloud 的 API,它对巨大的列表进行分页(又名 returns 逐块)。
您的 API 不分页,因此您一次收到所有内容。
查看来自 SoundCloud 的示例结果 API:
{
"collection": [
{
"kind": "track",
"id": 613085994,
"created_at": "2019/04/29 13:25:27 +0000",
"user_id": 316489116,
"duration": 476281,
[...]
},
[...]
],
"next_href": "https://api.soundcloud.com/users/8665091/favorites?client_id=caf73ef1e709f839664ab82bef40fa96&cursor=1550315585694796&linked_partitioning=1&page_size=10"
}
next_href
中的URL给出了下一批物品的URL(URL相同,但'cursor'参数不同每一次)。这就是使无限列表起作用的原因:每次,它都会获取下一批。
您必须实施服务器端分页才能实现此结果。
回应您的编辑:
UPDATED MY QUESTION
How can I set these places commented in the code?
/*if(this.state.nextHref) { url = this.state.nextHref; }*/
and
/*if(resp.next_href) { self.setState({ todos: todos, nextHref: resp.next_href });*/
忘记加载 nextHref,你没有那个。您拥有的是当前页码(您已将内容加载到哪一页)。
尝试以下方法:
if (!self.state.finishedLoading) {
qwest.get("https://your-api.org/json", {
page: self.state.nextPage,
'per-page': 10,
}, {
cache: true
})
.then((xhr, resp) => {
let {todos, nextPage} = self.state;
if(resp) {
self.setState({
todos: [...todos, ...resp],
nextPage: nextPage + 1,
finishedLoading: resp.length > 0, // stop loading when you don't get any more results
});
}
});
}
一些挑剔
除了使用具有副作用的 Array.map
(并且仅针对该副作用)之外,您还改变了 self.state
(bad):
var todos = self.state.todos;
resp.map((todo) => {
todos.push(todo);
});
你可以这样写:
var todos = [self.state.todos, ...resp];
此处地图的用法相同:
var items = [];
this.state.todos.map((todo, i) => {
items.push(
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>
);
});
应该写成:
const items = this.state.todos.map((todo, i) =>
<div className="track" key={todo.id}>
<p className="title">{todo.title}</p>
</div>);