英文:
Next.js 13 pattern for changeable API fetch request
问题
我有一个看似简单的Next.js应用目录模式,我希望用户能够浏览从服务器获取的数据的不同页面。请看下面的一个简单示例,我希望用户可以更改`currentPage`的状态,并将其传递给我的文章获取请求。对于这种模式,最佳实践是什么?下面的当前设置会导致无限循环问题,因为`listArticles`需要在服务器组件中调用,但我无法将动态页面值传递给获取请求。我如何将此页面传递给获取请求?
**page.tsx**
```jsx
// 父组件
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
// 子服务器组件
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>
))
}
</>
)
}
<details>
<summary>英文:</summary>
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>
))
}
</>
)
}
</details>
# 答案1
**得分**: 1
这是如何处理的方法是通过将当前页面持久化在查询中,然后使用`<Link />`组件将用户重定向到正确的URL。这允许您完全放弃客户端组件,专门用于此特定用例。
##### 非常简单的实现示例
正如您所看到的,当解析当前页面时,我使用了[nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing),这确保了如果没有提供页面,则`currentPage`变量始终为`1`。
```jsx
import Link from "next/link";
export default async function Page(props) {
const params = props.searchParams;
const currentPage = parseInt(params.get("page") ?? "0", 10);
return (
<>
 {/* 您的JSX的其余部分 */}
<ArticleList currentPage={currentPage} />
<Link href={{ pathname: "/articles", query: { page: currentPage - 1 } }}>
<button>上一页</button>
</Link>
<Link href={{ pathname: "/articles", query: { page: currentPage + 1 } }}>
<button>下一页</button>
</Link>
 {/* 您的JSX的其余部分 */}
</>
);
}
此外,我还在您提供的初始代码片段中看到了一个错误,您不能在服务器组件内使用钩子(如useState
)。
英文:
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.
Very simple implementation example
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论