如何使用 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) =&gt; 应该在&lt;Pagination/&gt; 组件内? @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 个项目以及如何将其放入 &lt;Pagination&gt; 组件中。 @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 数据进行分页的主要内容,如果未能解决你的问题,请参考以下文章