Reputation: 1665
I have a seemingly simple Next.js app directory pattern where I want the ability for a user to cycle through pages of data fetched from the server. Take a simple example below, where I want the user-changeable state of currentPage
to be passed to my article fetch request. What is the best practice for this pattern? The current setup below will cause an infinite loop issue because listArticles
needs to be called in a server component, but then I cannot pass a dynamic page value to the fetch request. How do I pass this page to the fetch request?
page.tsx
// Parent
export default async function Page() {
const [currentPage, setCurrentPage] = useState(0);
return (
<>
<h3>Articles</h3>
<ArticleList
currentPage={currentPage}
/>
<button onClick={() => setCurrentPage(currentPage - 1)}>Previous page</button>
<p>Current page: {currentPage}</p>
<button onClick={() => setCurrentPage(currentPage + 1)}>Next page</button>
</>
)
}
ArticleList.tsx
// Child server component
export default async function ArticleList({
page = 0,
}: {
page?: number;
}) {
const listArticles = async () => {
const res = await fetch(`${process.env.NODE_ENV === "development" ? SITE.URLS.TEST : SITE.URLS.LIVE}/api/articles?&page=${page}`, {
method: "GET",
next: { revalidate: 5 },
});
if (!res.ok) {
console.error("Error fetching articles:");
console.error(res);
} else {
return res.json();
}
}
let data = await listArticles();
return (
<>
{
data.articles.map((article: any) => (
<div key={article.id}>
{article.id}
</div>
))
}
</>
)
}
Upvotes: 0
Views: 392
Reputation: 2863
The way you would go about this is by persisting the current page inside the query and then using the <Link />
component to redirect the user to the correct url. This allows you to completely renounce client components for this specific use case.
As you see I use nullish coalescing in when parsing the current page, this ensures that the currentPage
variable is always 1
if no page was provided.
import Link from "next/link";
export default async function Page(props) {
const params = props.searchParams;
const currentPage = parseInt(params.get("page") ?? "0", 10);
return (
<>
{/* Rest of your JSX */}
<ArticleList currentPage={currentPage} />
<Link href={{ pathname: "/articles", query: { page: currentPage - 1 } }}>
<button>Previous page</button>
</Link>
<Link href={{ pathname: "/articles", query: { page: currentPage + 1 } }}>
<button>Next page</button>
</Link>
{/* Rest of your JSX */}
</>
);
}
Also I see an error in the initial code snippet you have provided, you can not use hooks (like useState
) inside a server component.
Upvotes: 1