0

As I am starting my experience with Firebase I am a little bit struggling with the pagination of posts on my blog website :(.

I think I kind of understood the docs from Google and I know how to move the pagination to the next page. However, I have absolutely no clue how to paginate back to the previous page.

Basically, I wanted to have a simple pagination component which will look something like that: < 1 2 3 [...] > (where you can paginate next and back using the arrows).

It is fine to paginate to the next page but, when it comes to paginating back I cannot find any proper tutorial to do it in pure React.

I have tried to use various methods from startAt, endAt, endBefore etc. But the result was or an error or it was moving me back to the first page (even when I was on the third or fourth)

I even tried to find the first object in an array and use it as endBefore but it resulted again in paginating back to the first page.

That's how my code looks right now (yes I know that pageNext() and pagePrev() are the same)

import React, { Component } from 'react'
import { withFirebase } from './Firebase'
import Post from './Post'

import '../scss/Post.scss'

class Posts extends Component {
    constructor(props) {
        super(props);
        this.state = {
            loading:false,
            posts:[],
            post_id:[],
            lastVisible:null,
            limit:2
        }

        this.handlePageNext = this.handlePageNext.bind(this);
    }
    componentDidMount() {
        let newPosts=[];
        let postsId=[];

        this.setState({ loading: true });

        this.props.firebase.posts()
        .orderBy('date', 'desc')
        .limit(2)
        .get().then(querySnapshot => {
            let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
            this.setState({ lastVisible: lastVisible});

            querySnapshot.forEach(doc => {
                newPosts = newPosts.concat(doc.data());
                postsId = postsId.concat(doc.id);           
                this.setState({
                    posts:newPosts,
                    post_id:postsId,
                    loading:false
                });
            })
        })



    }

    handlePageNext() {
        let newPosts=[];
        let postsId=[];

        this.setState({ loading: true });

        this.props.firebase.posts()
        .orderBy('date', 'desc')
        .startAt(this.state.lastVisible)
        .limit(this.state.limit)
        .get().then(querySnapshot => {
            let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];

            this.setState({ lastVisible:lastVisible });
            querySnapshot.forEach(doc => {
                newPosts = newPosts.concat(doc.data());
                postsId = postsId.concat(doc.id);           
                this.setState({
                    posts:newPosts,
                    post_id:postsId,
                    loading:false
                });
            })
        })
    }

    handlePagePrev() {
        let newPosts=[];
        let postsId=[];

        this.setState({ loading: true });

        this.props.firebase.posts()
        .orderBy('date', 'desc')
        .startAt(this.state.lastVisible)
        .limit(this.state.limit)
        .get().then(querySnapshot => {
            let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];

            this.setState({ lastVisible:lastVisible});
            querySnapshot.forEach(doc => {
                newPosts = newPosts.concat(doc.data());
                postsId = postsId.concat(doc.id);           
                this.setState({
                    posts:newPosts,
                    post_id:postsId,
                    loading:false
                });
            })
        })
    }

    render() {
        return (
            <div className='posts'>
                <div className='row'>
                    {this.state.posts.map((post, i) => (
                        <Post 
                            key={i}
                            title={post.title}
                            author={post.author}
                            desc={post.desc}
                            text={post.text}
                            id={this.state.post_id[i]}
                            date={post.date}
                            imgURL={post.imgURL}/>
                    ))}

                    {this.state.loading && <p>Loading...</p>}
                    <button className='btn' onClick={() => this.handlePagePrev()}>&larr;</button>
                    <button className='btn' onClick={() => this.handlePageNext()}>></button>

                </div>

            </div>
        )
    }
}

export default withFirebase(Posts);

I wanted to have a simple pagination using buttons (left and right arrows) but I am struggling with it for already 3rd hour and cannot find the proper solution to this.

jhrwekuh
  • 186
  • 2
  • 15
  • 1
    If you understand Android, you can take a look **[here](https://stackoverflow.com/questions/50741958/how-to-paginate-firestore-with-android)**. – Alex Mamo Jun 18 '19 at 08:10

2 Answers2

1

You have to keep the "lastVisible" and pass it to startAfter(). 2 functions I wrote below:

export const getMostRecentPostsFirstPage = (limit, specificUserId) => {
  if (!Number.isInteger(limit) || limit < 1) {
    throw new Error('limit must be a positive integer');
  }

  const collection = Firestore.collection('posts');
  let query = null;

  if (specificUserId) {
    query = collection
      .where('userId', '==', `${specificUserId}`)
      .orderBy('postedTimestamp', 'desc')
      .limit(limit);
  } else {
    query = collection.orderBy('postedTimestamp', 'desc').limit(limit);
  }

  return new Promise((resolve, reject) => {
    const posts = [];
    query
      .get()
      .then(snapshot => {
        const lastVisible = snapshot.docs[snapshot.docs.length - 1];
        snapshot.forEach(post => {
          posts.push(post.data());
        });
        const hasMore = posts.length == limit;
        resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
      })
      .catch(error => reject(error));
  });
};
export const getMostRecentPostsNextPage = (lastVisible, limit, specificUserId) => {
  if (!lastVisible) {
    throw new Error('Need to provide lastVisible argument');
  }

  if (!Number.isInteger(limit) || limit < 1) {
    throw new Error('limit must be a positive integer');
  }

  const collection = Firestore.collection('posts');
  let query = null;

  if (specificUserId) {
    query = collection
      .where('userId', '==', `${specificUserId}`)
      .orderBy('postedTimestamp', 'desc')
      .startAfter(lastVisible)
      .limit(limit);
  } else {
    query = collection
      .orderBy('postedTimestamp', 'desc')
      .startAfter(lastVisible)
      .limit(limit);
  }

  return new Promise((resolve, reject) => {
    const posts = [];
    query
      .get()
      .then(snapshot => {
        const lastVisible = snapshot.docs[snapshot.docs.length - 1];
        snapshot.forEach(post => {
          posts.push(post.data());
        });
        const hasMore = posts.length == limit;
        resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
      })
      .catch(error => reject(error));
  });
};

It uses redux-saga, but you get the idea.

on first query, do not call "startAfter()", but do on the subsequent queries, and you must save "lastVisible" between each call.

user2343647
  • 633
  • 6
  • 17
  • Thanks for your help, I will try to manage it by myself and if I could not be able to do it I will simply implement just a button to see the older posts. – jhrwekuh Jun 17 '19 at 14:18
  • In my case, it was not actual pagination, it was to implement infinite scrolling, so to see older posts the user just scrolled up. Good luck and don't give up! – user2343647 Jun 17 '19 at 14:22
  • 1
    I think I will use the infinite scrolling, which is activated by button as I cannot find the solution for moving back to the previous page. There are queries and limitToLast method and I will try to dig a little bit deeper in that but I think this case would do the best with the infinite scrolling as it is just for the learning purposes :P – jhrwekuh Jun 17 '19 at 14:23
0

Here is standard pagination by using Firebase in reactjs.

Diamond
  • 3,470
  • 2
  • 19
  • 39