Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Implement Infinite Scroll in Next JS SSG?

I created a markdown blog by following the Next Js documentation and Using Typescript. To Fetch a list of blog posts I used getStaticProps as the docs suggested. I tried with some npm packages, it's not working, or maybe I didn't know how to set up perfectly.

If there is a good plugin or custom hooks, Please tell me how can I add the infinite scroll feature in Next Js static site generation.

Project source code is available in this GitHub repository: https://github.com/vercel/next-learn-starter/blob/master/typescript-final/pages/index.tsx

like image 550
Mejan Avatar asked Dec 23 '22 17:12

Mejan


1 Answers

I did some research and created my own react hooks to implement infinite scroll in Next JS. So, I am answering my question.

If anyone has a better method, please don't forget to share it here.

I found two working methods. First One done by using innerHeight, scrollTop, offsetHeight values. It worked perfectly, but somehow I felt this is not the correct way to do that. Please correct me if I am wrong.

The second method was to asynchronously observe changes in the intersection of a target element by Intersection Observer API. First, I created a pagination custom react hook to limit my blog posts then I used IntersectionObserver to fetch the blog posts of the next pages.

usePagination.tsx:

import { useState } from "react";

function usePagination(data, itemsPerPage: number) {
  const [currentPage, setCurrentPage] = useState(1);

  const maxPage = Math.ceil(data.length / itemsPerPage);

  function currentData() {
    const begin = (currentPage - 1) * itemsPerPage;
    const end = begin + itemsPerPage;
    return data.slice(null, end);
  }

  function next() {
    setCurrentPage((currentPage) => Math.min(currentPage + 1, maxPage));
  }

  return { next, currentData, currentPage, maxPage };
}

export default usePagination;

Index.tsx:

import { GetStaticProps } from "next";
import { getSortedPostsData } from "../lib/posts";
import Layout from "../components/layout";
import usePagination from "../lib/Hooks/usePagination";
import { useRef, useState, useEffect } from "react";

export default function Home({
  allPostsData,
}: {
  allPostsData: {
    id: string;
    title: string;
    date: string;
    cover: string;
  }[];
}) {
  const { next, currentPage, currentData, maxPage } = usePagination(
    allPostsData,
    8
  );
  const currentPosts = currentData();
  const [element, setElement] = useState(null);

  const observer = useRef<IntersectionObserver>();
  const prevY = useRef(0);
  useEffect(() => {
    observer.current = new IntersectionObserver(
      (entries) => {
        const firstEntry = entries[0];
        const y = firstEntry.boundingClientRect.y;

        if (prevY.current > y) {
          next();
        }
        prevY.current = y;
      },
      { threshold: 0.5 }
    );
  }, []);

  useEffect(() => {
    const currentElement = element;
    const currentObserver = observer.current;

    if (currentElement) {
      currentObserver.observe(currentElement);
    }

    return () => {
      if (currentElement) {
        currentObserver.unobserve(currentElement);
      }
    };
  }, [element]);
  return (
    <Layout>

      <article className="w-full mt-12 flex flex-wrap items-center justify-center">
        {currentPosts &&
          currentPosts.map(({ id, title, date, cover }) => (
            <div key={id} // ......
            </div>
          ))}
      </article>
      {currentPage !== maxPage ? (
        <h1 ref={setElement}>Loading Posts...</h1>
      ) : (
        <h1>No more posts available...</h1>
      )}
    </Layout>
  );
}

export const getStaticProps: GetStaticProps = async () => {
  const allPostsData = getSortedPostsData();

  return {
    props: {
      allPostsData,
    },
  };
};
like image 172
Mejan Avatar answered Jan 06 '23 15:01

Mejan