【问题标题】:How to paginate Cloud Firestore data with ReactJs如何使用 ReactJs 对 Cloud Firestore 数据进行分页
【发布时间】: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>
    )
}
}

感谢您的帮助!

【问题讨论】:

    标签: javascript reactjs firebase google-cloud-firestore


    【解决方案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】:

    对于 FirestoreFirestore PaginationReactJS 的新手来说,理解分页将如何工作或何时触发对 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 width="100" />
              <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

      【讨论】:

        猜你喜欢
        • 2020-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-21
        • 2018-11-17
        相关资源
        最近更新 更多