[英]How to paginate Cloud Firestore data with ReactJs
I'm working with Firebase - Cloud Firestore and at the moment I would like to paginate all the records available.我正在使用 Firebase - Cloud Firestore,目前我想对所有可用记录进行分页。 I already have a list of records and what is left is some pagination for this.我已经有一个记录列表,剩下的就是一些分页。 I'm new with Cloud Firestore, so any clarity is appreciated.我是 Cloud Firestore 的新手,所以任何清晰度都值得赞赏。
I checked the Firestore documentation ( https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query ) and examples with ReactJS, but there is not much available.我检查了 Firestore 文档 ( https://firebase.google.com/docs/firestore/query-data/query-cursors#paginate_a_query ) 和 ReactJS 的示例,但可用的不多。
I understand that eg: .startAt(0), .limit(10)
, but the question is how to paginate properly with this component called at the render method.我明白,例如: .startAt(0), .limit(10)
,但问题是如何使用在 render 方法中调用的这个组件正确分页。
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>
)
}
}
Thank you for the help!感谢您的帮助!
Pagination can be achieved using startAt()
可以使用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()
}
Anyone new to Firestore and Firestore Pagination with ReactJS that would be kinda confusing to understand how Pagination will work or when to trigger call to next set of documents in firestore.任何不熟悉Firestore和Firestore Pagination与ReactJS 的人都会有点难以理解 Pagination 将如何工作或何时触发对 firestore 中下一组文档的调用。 anyone struggle like this try my example to make some ideas and process ahead.(Im using React-Bootstrap to render UI Elements)任何人都像这样努力尝试我的例子来提出一些想法并提前处理。(我使用React-Bootstrap来呈现 UI 元素)
01 - Install Package react-infinite-scroll-component 01 - 安装包react-infinite-scroll-component
First Install this package yarn add react-infinite-scroll-component
首先安装这个包yarn add react-infinite-scroll-component
02 - Include Package 02 - 包括包裹
Include it to your file by 'import InfiniteScroll from 'react-infinite-scroll-component';'
通过'import InfiniteScroll from 'react-infinite-scroll-component';'
其包含到您的文件中'import InfiniteScroll from 'react-infinite-scroll-component';'
importing it进口
03 - Init State 03 - 初始状态
initiate state with empty list array使用空列表数组启动状态
this.state = {
list: [],
};
04 - Create Function to get first set of data and initiate it with component did mount 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 - Create function to get balance data set 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 - Render UI 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 width="100" />
<h2>{ single.name }</h2>
</div>
</Col>
))}
</Row>
</InfiniteScroll>
Use startAt() or startAfter() for that为此使用 startAt() 或 startAfter()
firestore
.collection("Users")
.startAt(0)
.limit(10)
.get()
//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();
}, []);
After loading initial data use following function for next button trigger加载初始数据后使用以下功能进行下一个按钮触发
//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();
}
};
Then Previous button function然后上一个按钮功能
//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();
};
at the end create list view & two buttons like this最后创建列表视图和两个这样的按钮
{
//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>
}
That's it check my code comments where you can change as per your need.这就是检查我的代码注释,您可以根据需要进行更改。 this is what happens when you paginate using Firebase FireStore.这就是使用 Firebase FireStore 进行分页时会发生的情况。 you can use create custom hook to reuse these component as per your need.您可以根据需要使用创建自定义钩子来重用这些组件。
Hope this could help someone so i made a gist check it here希望这可以帮助某人所以我在这里做了一个要点检查
here AddTable and AddForm is adding table and add form to fill data in table...这里AddTable和AddForm是添加表格和添加表格以填充表格中的数据...
import React, { useEffect, useState } from "react";
import Button from "react-bootstrap/Button";
import Pagination from "react-bootstrap/Pagination";
import AddTable from "../management/AddTable";
import AddForm from "../management/AddSuperAdminForm";
import {
where,
getDocs,
collection,
query,
orderBy,
startAfter,
limit,
endBefore,
limitToLast,
} from "firebase/firestore";
import { db_firestore } from "../../../firebase.config";
const SuperAdmin = () => {
const [tableDataArray, setTableDataArray] = useState();
const [show, setShow] = useState(false);
const [editId, setEditId] = useState("");
const [oldUid, setOldUid] = useState("");
const [lastVisible, setLastVisible] = useState();
const [prevVisible, setPrevVisible] = useState();
const handleClose = () => {
setShow(false);
setEditId("");
};
const handleShow = () => {
setShow(true);
setEditId("");
};
let tempdata;
let pageSize = 3;
let q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
limit(pageSize)
);
function nextPage(lastVisible) {
q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
startAfter(lastVisible),
limit(pageSize)
);
}
function prevPage(firstVisible) {
q = query(
collection(db_firestore, "users"),
where("role", "==", "superadmin"),
orderBy("timestamps", "desc"),
endBefore(firstVisible),
limitToLast(pageSize + 1)
);
}
const newfun = async () => {
const querySnapshot = await getDocs(q);
tempdata = [];
// Get the last visible document
setLastVisible(querySnapshot.docs[querySnapshot.docs.length - 1]);
// Get the prev visible document
setPrevVisible(querySnapshot.docs[0]);
querySnapshot.forEach((doc) => {
const { name, email, uid } = doc.data();
tempdata.push([name, email, uid, doc.id]);
});
console.log("SuperAdmin...");
setTableDataArray(tempdata);
};
useEffect(() => {
newfun();
// setInterval(() => { // if you want to get new update after some secound
// newfun();
// }, 10000);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Button
className="d-block mx-auto my-2"
variant="primary"
onClick={handleShow}
>
Add SuperAdmin
</Button>
{/* -----> AddTable <------
Index will generate Automatic In Table.
Always keep action end of the table.
*/}
{tableDataArray ? (
<AddTable
tableHeaders={["Name", "Email", "uid", "Action"]}
tableData={tableDataArray}
fetchNew={newfun}
setEditId={setEditId}
setShow={setShow}
setOldUid={setOldUid}
/>
) : (
""
)}
<AddForm
fetchNew={newfun}
show={show}
setShow={setShow}
handleClose={handleClose}
editId={editId}
oldUid={oldUid}
/>
<Pagination className="float-end">
<Pagination.Item
className="shadow-none"
size="lg"
onClick={() => {
prevPage(prevVisible);
newfun();
}}
>
Previous
</Pagination.Item>
<Pagination.Item
className="shadow-none"
size="lg"
onClick={() => {
nextPage(lastVisible);
newfun();
}}
>
Next
</Pagination.Item>
</Pagination>
</div>
);
};
export default SuperAdmin;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.