Next.js 13 可变 API 获取请求的模式

huangapple go评论75阅读模式
英文:

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}
/>

        &lt;button onClick={() =&gt; setCurrentPage(currentPage - 1)}&gt;Previous page&lt;/button&gt;
        &lt;p&gt;Current page: {currentPage}&lt;/p&gt;
        &lt;button onClick={() =&gt; setCurrentPage(currentPage + 1)}&gt;Next page&lt;/button&gt;
    &lt;/&gt;
)

}

**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 === &quot;development&quot; ? SITE.URLS.TEST : SITE.URLS.LIVE}/api/articles?&amp;page=${page}, {
method: "GET",
next: { revalidate: 5 },
});

    if (!res.ok) {
        console.error(&quot;Error fetching articles:&quot;);
        console.error(res);
    } else {
        return res.json();
    }
}

let data = await listArticles();

return (
    &lt;&gt;
        { 
            data.articles.map((article: any) =&gt; (
                &lt;div key={article.id}&gt;
                    {article.id}
                &lt;/div&gt;
            ))
        }
    &lt;/&gt;
)

}



</details>


# 答案1
**得分**: 1

这是如何处理的方法是通过将当前页面持久化在查询中,然后使用`&lt;Link /&gt;`组件将用户重定向到正确的URL。这允许您完全放弃客户端组件,专门用于此特定用例。

##### 非常简单的实现示例

正如您所看到的,当解析当前页面时,我使用了[nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing),这确保了如果没有提供页面,则`currentPage`变量始终为`1`。

```jsx
import Link from &quot;next/link&quot;;

export default async function Page(props) {
  const params = props.searchParams;
  const currentPage = parseInt(params.get(&quot;page&quot;) ?? &quot;0&quot;, 10);
  return (
    &lt;&gt;
     &#160;{/* 您的JSX的其余部分 */}

      &lt;ArticleList currentPage={currentPage} /&gt;

      &lt;Link href={{ pathname: &quot;/articles&quot;, query: { page: currentPage - 1 } }}&gt;
        &lt;button&gt;上一页&lt;/button&gt;
      &lt;/Link&gt;

      &lt;Link href={{ pathname: &quot;/articles&quot;, query: { page: currentPage + 1 } }}&gt;
        &lt;button&gt;下一页&lt;/button&gt;
      &lt;/Link&gt;

     &#160;{/* 您的JSX的其余部分 */}
    &lt;/&gt;
  );
}

此外,我还在您提供的初始代码片段中看到了一个错误,您不能在服务器组件内使用钩子(如useState)。

英文:

The way you would go about this is by persisting the current page inside the query and then using the &lt;Link /&gt; 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 &quot;next/link&quot;;

export default async function Page(props) {
  const params = props.searchParams;
  const currentPage = parseInt(params.get(&quot;page&quot;) ?? &quot;0&quot;, 10);
  return (
    &lt;&gt;
     &#160;{/* Rest of your JSX */}

      &lt;ArticleList currentPage={currentPage} /&gt;

      &lt;Link href={{ pathname: &quot;/articles&quot;, query: { page: currentPage - 1 } }}&gt;
        &lt;button&gt;Previous page&lt;/button&gt;
      &lt;/Link&gt;

      &lt;Link href={{ pathname: &quot;/articles&quot;, query: { page: currentPage + 1 } }}&gt;
        &lt;button&gt;Next page&lt;/button&gt;
      &lt;/Link&gt;

     &#160;{/* Rest of your JSX */}
    &lt;/&gt;
  );
}

Also I see an error in the initial code snippet you have provided, you can not use hooks (like useState) inside a server component.

huangapple
  • 本文由 发表于 2023年7月18日 01:51:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定