Last week, I added some features to my blog. From the preloader that utilized Next.js' router API, to the search and Table of Content component.
Recently, I've just added a minor feature that allows me to conditionally render a certain amount of articles on my blog. Instead of having all the articles I've written all at once on the page.
This feature is a bit common on some developer blogging platforms., and building it was a bit straightforward since I knew what needs to be done.
The idea behind it is for us to have an initial amount of articles listed whenever anyone navigates to the blog route. So, a variable like the one below should suffice
const initialPostList = 6
And whenever a visitor clicks on the "load more" button, a callback function is triggered to increment the number of articles/posts on the blog.
It'll be ideal to also have another variable that holds the increment value. A variable like the one below should work fine.
const incrementInitialPostList = 4
Watching the state
The snippets in the previous section are of great importance to this feature. initialPostList
holds the amount of articles we want on initial render of the blog page, while incrementInitialPostList
holds the value with which the articles list would be incremented.
With that said, some of the snippets here may look unfamiliar or intricate. If you happen to feel this way, when you're reading this, I'd implore you to check out this article that walks you through how to build a Next.js blog
I'm assuming you already have an idea of some Next.js data-fetching patterns, If not, please, take a look at the link I shared in the paragraph above.
I needed a way to keep these values — initialPostList
and incrementInitialPostList
— in the blog component's state, so that I can tap into its lifecycle methods .
The snippet below shows what the Blog component looks like.
import Article from './Article'; // Replace with your own Article component
const initialPostList = 6; // Number of articles to display initially
const incrementInitialPostList = 4; // Number of articles to add each time the "load more" button is clicked
export default function Blog() {
const [displayPosts, setDisplayPosts] = React.useState(initialPostList);
const [articles, setArticles] = React.useState(/* Your array of articles goes here */);
const loadMore = () => {
setDisplayPosts(displayPosts + incrementInitialPostList)
}
return (
<div>
{articles.slice(0, displayPosts).map(article => (
<Article key={article.id} article={article} />
))}
{displayPosts < articles.length ? (
<button onClick={handleLoadMore}>Load More</button>
) : null}
</div>
);
}
Rendering more posts
In the snippet from the previous section, I'm using initialPostList
variable to render the first 6 articles and adding the incrementInitialPostList
variable to represent the number of articles to render each time the "load more" button is clicked.
The loadMore
function simply updates the displayPosts
state variable with the new value.
The articles array can come from any data source you like (e.g. an API endpoint, a static file, etc.), and you can customize the Article component to match the structure of your data.
But, in this case, it is coming from a markdown source, and I can get the list of articles with getStaticProps
, a data-fetching method of Next.js
The blog component is modified like so;
export default function Blog({ posts }) {
const [displayPosts, setDisplayPosts] = React.useState(initialPostList);
const [articles, setArticles] = React.useState(posts);
const loadMore = () => {
setDisplayPosts(displayPosts + incrementInitialPostList)
}
return (
<div>
{articles.slice(0, displayPosts).map(article => (
<Article key={article.id} article={article} />
))}
{displayPosts < articles.length ? (
<button onClick={loadMore}>Load More</button>
) : null}
</div>
);
}
export async function getStaticProps() {
let articles = await getAllArticles()
const sortedArticles = articles.map((article) => article)
sortedArticles.sort((a, b) => {
return new Date(b.publishedAt) - new Date(a.publishedAt)
})
return {
props: {
posts: sortedArticles,
},
}
}
The slice function is used to only display the first item in the posts
array through the value of the displayPosts
value — which is 6 — from the articles array.
The displayPosts < articles.length
condition is used to check if there are more articles to display and show the "load more" button accordingly.
From @nlxdodge's comment below, I found an approach that helps you preserve the state when you navigate to, and away from the page.
Take a look at it here
Oldest comments (2)
Nice post, while I do like the Read More from a more simple perspective, also know that apps like Reddit also have this. But with infinite content the page/app becomes really slow.
So before implementing this feature, think about if this can impact peformance.
And does it also save the state? If you click on a link and click back. Does it reset and you have to click Read More again a couple of times to go back where you where? Then I would opt for the pagination approach instead.
Just my 2 cents as a Backend Developer 🙃
Oh! This is such a great input!!
I also thought of the infinite scroll feature, and I think this may be sort of related right? The pagination approach also sound superb!
About the state being in sync. I actually checked, and it isn't. Wow! this is such a nice observation. I'll look for a workaround.