1
I started building a Next.js frontend for a WordPress website, using the App Router and GraphQL plugin to fetch data from WordPress. The homepage is all set up to display the latest posts, and it’s a server component.
import PostCard from "./components/post";
import styles from './page.module.scss'
interface Post {
title: string;
date: string;
excerpt: string;
featuredImage: {
node: {
sourceUrl: string;
};
};
categories: {
edges: {
node: {
name: string;
};
}[];
};
slug: string;
author: {
node: {
name: string;
};
};
}
async function getPosts(cursor: string): Promise<Post[]> {
const endpoint = process.env.WORDPRESS_API_URL || '';
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
query GetPosts($cursor: String!) {
posts(first: 10, after: $cursor) {
nodes {
title
date
excerpt
featuredImage {
node {
sourceUrl
}
}
categories {
edges {
node {
name
}
}
}
slug
author {
node {
name
}
}
}
}
}
`,
variables: {
cursor: cursor,
},
}),
});
const data = await response.json();
return data.data.posts.nodes;
}
export default async function HomePage() {
const posts = await getPosts("");
return (
<div>
<div className={styles.postsList}>
{posts.map((post) => (
<PostCard post={post} key={post.slug}/>
))}
</div>
<button onClick={ }>Load More</button>
</div>
);
}
The challenge is loading the next batch of posts right after this cursor (which is ID of the last post I loaded by GraphQL) without using React’s useState
or useEffect
hooks, as these do no work on server components.
How can I make the "Load More" button work with server components?
I’ve tried to tackle pagination using query strings, but that plan fizzled out because GraphQL’s WordPress plugin exclusively works with cursor-based pagination.
1 Answer
Reset to default
0
Server components and client components can be combined in the same component tree, which is what you should be doing here: Separate the server part from the client part in two separated components.
Most of what you posted is the server part, and remains mostly the same, with some minor changes to use the client component inside HomePage
:
export default async function HomePage() {
const posts = await getPosts("");
return (
<div>
<div className={styles.postsList}>
{ posts.map((post) => (
<PostCard post={ post } key={ post.slug } />
)) }
</div>
<MorePosts>
</div>
);
}
Then, you’ll have the client component, which would look something like this:
'use client'
export const MorePosts = () => {
// This is just some placeholder / pseudo-code for whatever custom logic
// you need to fetch these:
const { morePosts, loadMorePosts, areMorePostsLoading } = useMorePosts();
return (
<>
<div className={ styles.morePostsList }>
{ morePosts.map((post) => (
<PostCard post={ post } key={ post.slug } />
)) }
</div>
<button
onClick={ loadMorePosts }
disabled={ areMorePostsLoading }>
Load More
</button>
</>
)
}
Note useMorePosts()
represents a custom hook that probably uses useEffect
and useState
plus fetch
, axios
or some other client-side request library to load additional posts in pages / batches and store them, plus manage the associated status (loading, error, cursor position…).
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
|