英文:
How do I create a simple server component that can fetch data in NextJS 13?
问题
I am learning NextJS. Currently, I am using NextJS 13 with the new app directory instead of the old pages directory. I have tried various ways of fetching data, but I have not gotten any of them to work. It should be as simple as
src/app/page.tsx:
export interface ToDo {
userId: number;
id: number;
title: string;
completed: boolean;
}
const getToDos = async (): Promise<ToDo[]> => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
console.log(`Status: ${res.status}`); // prints 'Status: 200'
return res.json();
}
const Home = async () => {
const toDos = await getToDos();
return (
<ul>
{toDos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
export default Home;
The JSON returned by the endpoint is
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This is the output. Notice how I am getting both a 200 OK status and an error message:
Warning: Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
Status: 200
- ┌ GET / 200 in 606ms
│
└──── GET https://jsonplaceholder.typicode.com/todos/1 200 in 67ms (cache: HIT)
- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
If I add "use server" to the beginning of page.tsx, I get a compilation error:
Server Actions require `experimental.serverActions` option to be enabled in your Next.js config: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
Ideally, I would like to statically generate HTML, but for now, I would be grateful for any working fetching code.
Update
One of the previous solutions I tried was to use Tanstack Query. I have now removed all of that code from my layout.tsx. Now I get a different error for this code:
export default async function Home(): Promise<JSX.Element> {
const todos = await getToDos();
console.log(todos);
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
saying
error TypeError: todos.map is not a function
英文:
I am learning NextJS. Currently, I am using NextJS 13 with the new app directory instead of the old pages directory. I have tried various ways of fetching data, but I have not gotten any of them to work. It should be as simple as
src/app/page.tsx:
export interface ToDo {
userId: number;
id: number;
title: string;
completed: boolean;
}
const getToDos = async (): Promise<ToDo[]> => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
console.log(`Status: ${res.status}`); // prints 'Status: 200'
return res.json();
}
const Home = async () => {
const toDos = await getToDos();
return (
<ul>
{toDos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
export default Home;
The JSON returned by the endpoint is
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
This is the output. Notice how I am getting both a 200 OK status and an error message:
Warning: Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
<... client={{queryCache: ..., mutationCache: ..., logger: ..., defaultOptions: ..., queryDefaults: ..., mutationDefaults: ..., mountCount: ...}} children=...>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
{queryCache: {listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}, mutationCache: ..., logger: ..., defaultOptions: ..., queryDefaults: ..., mutationDefaults: ..., mountCount: ...}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Status: 200
- ┌ GET / 200 in 606ms
│
└──── GET https://jsonplaceholder.typicode.com/todos/1 200 in 67ms (cache: HIT)
- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
{listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}
^^^^^^^^
at stringify (<anonymous>)
null
- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
{listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}
^^^^^^^^
at stringify (<anonymous>)
digest: "501055159"
null
If I add "use server" to the beginning of page.tsx, I get a compilation error:
Server Actions require `experimental.serverActions` option to be enabled in your Next.js config: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
Ideally I would like to statically generate HTML, but for now I would be grateful for any working fetching code.
Update
One of the previous solutions I tried was to use Tanstack Query. I have now removed all of that code from my layout.tsx. Now I get a different error for this code:
export default async function Home(): Promise<JSX.Element> {
const todos = await getToDos();
console.log(todos);
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
saying
error TypeError: todos.map is not a function
答案1
得分: 1
似乎您正在尝试将一个来自服务器组件的函数传递给客户端组件,除非它是一个服务器动作,否则这是不可能的,因为服务器动作目前仍处于 alpha 版本,您需要通过在您的 next.config.js 文件中设置 experimental.serverActions
来启用它们:
module.exports = {
experimental: {
serverActions: true,
},
};
这是一个服务器端组件的示例:
import "server-only";
interface ToDo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function getToDos(): Promise<ToDo[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
return await res.json(); // 顺便提一下,您漏掉了一个 await
}
export default async function Home(): Promise<JSX.Element> {
const todos = await getToDos();
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
我强烈建议您安装 server-only 包,这样如果您尝试在客户端导入服务器组件,就会抛出错误。
英文:
It seems you are trying to pass a function from a server component to a client component, this is not possible unless it is a server action, since server actions are still a feature currently in alpha, you need to enable them by setting experimental.serverActions
in your next.config.js file:
module.exports = {
experimental: {
serverActions: true,
},
};
Here is an example of a server side component:
import "server-only";
interface ToDo {
userId: number;
id: number;
title: string;
completed: boolean;
}
async function getToDos(): Promise<ToDo[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
return await res.json(); // by the way, you were missing an await here
}
export default async function Home(): Promise<JSX.Element> {
const todos = await getToDos();
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
I strongly recommend you install the server-only package, so an error is thrown if you try to import a server component into the client side.
答案2
得分: 0
正如 @FabioNettis 正确指出的那样,我尝试获取一个单一对象,这不可迭代。我意外地尝试从路径 /todos/1
而不是 /todos
获取。因此,获取代码实际上是正确的,尽管不是很好。良好的错误处理会更容易调试。
英文:
As @FabioNettis correctly pointed out, I was trying to fetch a single object, which is not iterable. I had accidentally tried to fetch from the path /todos/1
rather than /todos
. So the fetching code was actually correct, although not great. Good error handling would have made debugging easier.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论