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
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,
},
};
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With