如何使用 ReactJs 对 Cloud Firestore 数据进行分页
Posted
技术标签:
【中文标题】如何使用 ReactJs 对 Cloud Firestore 数据进行分页【英文标题】:How to paginate Cloud Firestore data with ReactJs 【发布时间】:2019-04-02 08:14:02 【问题描述】:我正在使用 Firebase - Cloud Firestore,目前我想对所有可用记录进行分页。我已经有一个记录列表,剩下的就是一些分页。我是 Cloud Firestore 的新手,因此感谢您的澄清。
我查看了 Firestore 文档 (https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query) 和 ReactJS 示例,但可用的内容不多。
我知道例如:.startAt(0), .limit(10)
,但问题是如何使用在渲染方法中调用的这个组件正确分页。
import React, Component from 'react';
import Pagination from "react-js-pagination";
import firestore from "./Firebase";
export default class DataList extends Component
constructor(props)
super(props);
this.state =
dbItems: [],
currentPage: 1,
itemsPerPage: 3,
totalItemCount: 1,
activePage: 15
this.handlePageChange = this.handlePageChange.bind(this);
handlePageChange(pageNumber)
console.log(`active page is $pageNumber`);
this.setState( activePage: pageNumber );
async getItems()
const currentPage, itemsPerPage = this.state;
const startAt = currentPage * itemsPerPage - itemsPerPage;
const usersQuery = firestore.collection('Users').orderBy("email").startAt(startAt).limit(itemsPerPage)
const snapshot = await usersQuery.get()
const items = snapshot.docs.map(doc => doc.data())
return this.setState(
dbItems: items,
totalItemCount: firestore.collection('Users').get().then(res => console.log(res.size))
)
componentDidMount()
this.getItems()
componentDidUpdate(prevProps, prevState)
const isDifferentPage = this.state.currentPage !== prevState.currentPage
if (isDifferentPage) this.getItems()
render()
return (
<div>
this.state.dbItems.map((users, index) =>
return (
<p key=index>
<b>First Name:</b> users.firstname <br />
<b>Email:</b> users.email
</p>
)
)
<Pagination
activePage=this.state.activePage
itemsCountPerPage=this.state.itemsPerPage
totalItemsCount=this.state.totalItemCount
pageRangeDisplayed=this.state.itemsPerPage
onChange=this.handlePageChange
/>
</div>
)
感谢您的帮助!
【问题讨论】:
【参考方案1】:可以使用startAt()
实现分页
// Get Items.
async fetchUsers = () =>
// State.
const users, usersPerPage = this.state
// Last Visible.
const lastVisible = users && users.docs[users.docs.length - 1]
// Query.
const query = firestore.collection('Users')
.orderBy('email')
.startAfter(lastVisible)
.limit(usersPerPage)
// Users.
const users = await query.get()
// ..
return this.setState(users)
// Did Mount.
componentDidMount()
this.fetchUsers()
// Did Update.
componentDidUpdate(prevProps, prevState)
const isDifferentPage = this.state.currentPage !== prevState.currentPage
if (isDifferentPage) this.fetchUsers()
【讨论】:
这是在componentDidMount()
中应用的,对吗? @Arman Charan
我可能会将它打包为 getItems()
函数并在 componentDidMount()
和 handlePageChange()
或 componentDidUpdate()
中调用它(取决于您想要的用户体验)@RCohen
见上面的实际例子@RCohen
好吧,这看起来不错。也许this.state.dbItems.map((users, index) =>
应该在<Pagination/>
组件内? @Arman Charan
需要使用 OrderBy 也需要 startAt 不是偏移索引,它的开始位置基于使用的 OrderBy 字段。因此,如果在 orderBy 中使用了日期,则 startAt 中需要一个日期【参考方案2】:
对于 Firestore 和 Firestore Pagination 和 ReactJS 的新手来说,理解分页将如何工作或何时触发对 firestore 中下一组文档的调用会有点令人困惑。任何人都像这样挣扎尝试我的例子来提出一些想法和流程。(我使用React-Bootstrap 来呈现 UI 元素)
01 - 安装包react-infinite-scroll-component
首先安装这个包yarn add react-infinite-scroll-component
02 - 包含包
通过'import InfiniteScroll from 'react-infinite-scroll-component';'
导入将其包含到您的文件中
03 - 初始状态
用空列表数组初始化状态
this.state =
list: [],
;
04 - 创建函数以获取第一组数据并使用组件启动它确实挂载
//component did mount will fetch first data from firestore
componentDidMount()
this.getUsers()
getUsers()
let set = this
//initiate first set
var first = set.ref.collection("users").limit(12);
first.get().then(function (documentSnapshots)
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
//initiate local list
const list = [];
documentSnapshots.forEach(function(doc)
//im fetching only name and avatar url you can get any data
//from your firestore as you like
const name, avatar_full_url = doc.data();
//pushing it to local array
list.push( key: doc.id, name, avatar_full_url );
);
//set state with updated array of data
//also save last fetched data in state
set.setState( list, last: lastVisible );
);
05 - 创建函数以获取余额数据集
fetchMoreData = () =>
let set = this
//get last state we added from getUsers()
let last = this.state.last
var next = set.ref.collection("users").startAfter(last).limit(12);
next.get().then(function (documentSnapshots)
// Get the last visible document
var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
const list = [];
documentSnapshots.forEach(function(doc)
//im fetching only name and avatar url you can get any data
//from your firestore as you like
const name, avatar_full_url = doc.data();
list.push( key: doc.id, name, avatar_full_url );
);
//set state with updated array of data
//also save last fetched data in state
let updated_list = set.state.list.concat(list);
set.setState( list: updated_list, last: lastVisible );
);
;
06 - 渲染用户界面
<InfiniteScroll
dataLength=this.state.list.length
next=this.fetchMoreData
hasMore=true
loader=<span className="text-secondary">loading</span>>
<Row className="mt-3">
this.state.list.map((single, index) => (
<Col lg=4 key= index >
<div>
<Image src= single.avatar_full_url roundedCircle />
<h2> single.name </h2>
</div>
</Col>
))
</Row>
</InfiniteScroll>
【讨论】:
【参考方案3】:为此使用 startAt() 或 startAfter()
firestore
.collection("Users")
.startAt(0)
.limit(10)
.get()
【讨论】:
我明白了,关键是要抓住这些信息并正确分页。例如,每页 3 个项目以及如何将其放入<Pagination>
组件中。 @Orkhan Jafarov
您必须更改您的 activePage 状态并在页面上的项目上乘以页面,例如 startAt(2*10)
【参考方案4】:
查看此示例,这可以帮助任何尝试上一个/下一个分页的人
//initial state
const [list, setList] = useState([]);
const [page, setPage] = useState(1);
//loading initial data
useEffect(() =>
const fetchData = async () =>
await firebase.firestore().collection('users')
.orderBy('created', 'desc') //order using firestore timestamp
.limit(5) //change limit value as your need
.onSnapshot(function(querySnapshot)
var items = [];
querySnapshot.forEach(function(doc)
items.push( key: doc.id, ...doc.data() );
);
setList(items);
)
;
fetchData();
, []);
加载初始数据后,使用以下函数触发下一个按钮
//next button function
const showNext = ( item ) =>
if(list.length === 0)
//use this to show hide buttons if there is no records
else
const fetchNextData = async () =>
await firebase.firestore().collection('users')
.orderBy('created', 'desc') //order using firestore timestamp
.limit(5) //change limit value as your need
.startAfter(item.created) //we pass props item's first created timestamp to do start after you can change as per your wish
.onSnapshot(function(querySnapshot)
const items = [];
querySnapshot.forEach(function(doc)
items.push( key: doc.id, ...doc.data() );
);
setList(items);
setPage(page + 1) //in case you like to show current page number you can use this
)
;
fetchNextData();
;
然后是上一个按钮功能
//previous button function
const showPrevious = (item) =>
const fetchPreviousData = async () =>
await firebase.firestore().collection('users')
.orderBy('created', 'desc')
.endBefore(item.created) //this is important when we go back
.limitToLast(5) //this is important when we go back
.onSnapshot(function(querySnapshot)
const items = [];
querySnapshot.forEach(function(doc)
items.push( key: doc.id, ...doc.data() );
);
setList(items);
setPage(page - 1)
)
;
fetchPreviousData();
;
最后创建列表视图和两个这样的按钮
//list doc's here this will come inside return (place this code inside table)
list.map((doc) => (
<tr key=doc.key>
<td> doc.name </td>
<td> doc.age </td>
<td> doc.note </td>
</tr>
))
//show previous button only when we have items
//pass first item to showPrevious function
page === 1 ? '' :
<Button onClick=() => showPrevious( item: list[0] ) >Previous</Button>
//show next button only when we have items
//pass last item to showNext function
list.length < 5 ? '' :
<Button onClick=() => showNext( item: list[list.length - 1] )>Next</Button>
就是这样,检查我的代码 cmets,您可以根据需要进行更改。这就是您使用 Firebase FireStore 进行分页时发生的情况。您可以根据需要使用创建自定义挂钩来重用这些组件。
希望这可以帮助某人所以我做了一个要点检查it here
【讨论】:
以上是关于如何使用 ReactJs 对 Cloud Firestore 数据进行分页的主要内容,如果未能解决你的问题,请参考以下文章
javascript ReactJS:当用户停止输入时输入fire onChange(或按下Enter键)
Google Cloud Messaging (GCM) 可以与 Kindle Fire 搭配使用吗?