简体   繁体   English

如何使用 ReactJs 对 Cloud Firestore 数据进行分页

[英]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.任何不熟悉FirestoreFirestore PaginationReactJS 的人都会有点难以理解 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()

Check this example this could help anyone who trying previous / next pagination检查这个例子这可以帮助任何尝试上一页/下一页的人

//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...这里AddTableAddForm是添加表格和添加表格以填充表格中的数据...

  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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM