如何使用 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 数据进行分页的主要内容,如果未能解决你的问题,请参考以下文章

javascript ReactJS:当用户停止输入时输入fire onChange(或按下Enter键)

Google Cloud Messaging (GCM) 可以与 Kindle Fire 搭配使用吗?

使用 Mongo Cloud 从部署在 Heroku 上的 Strapi 获取数据到 ReactJs

如何在 Fire Base 中使用完成处理程序

如何从 Reactjs 中的 json 服务器中删除数据

如何使用 Ruby 作为后端将 React 上的图像上传到 Google Cloud Storage