How to paginate Cloud Firestore data with ReactJs

2020-06-19 10:22发布

问题:

I'm working with Firebase - Cloud Firestore and at the moment I would like to paginate all the records available. 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.

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.

I understand that eg:.startAt(0), .limit(10), but the question is how to paginate properly with this component called at the render method.

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!

回答1:

Pagination can be achieved using startAt()

// Get Items.
async getItems() {
  const {currentPage, itemsPerPage} = this.state
  const startAt = currentPage * itemsPerPage - itemsPerPage
  const query = firestore.collection('Users').startAt(startAt).limit(itemsPerPage)
  const snapshot = await query.get()
  const items = snapshot.docs.map(doc => doc.data())
  return this.setState({dbItems: items})
}

// Did Mount.
componentDidMount() {
  this.getItems()
}

// Did Update.
componentDidUpdate(prevProps, prevState) {
  const isDifferentPage = this.state.currentPage !== prevState.currentPage
  if (isDifferentPage) this.getItems()
}


回答2:

Use startAt() or startAfter() for that

firestore
 .collection("Users")
 .startAt(0)
 .limit(10)
 .get()


回答3:

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. anyone struggle like this try my example to make some ideas and process ahead.(Im using React-Bootstrap to render UI Elements)

01 - Install Package react-infinite-scroll-component

First Install this package yarn add react-infinite-scroll-component

02 - Include Package

Include it to your file by 'import InfiniteScroll from 'react-infinite-scroll-component';' importing it

03 - Init State

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

//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

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

<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>