Next.js管理页创建用户报错:仅允许普通对象从Server Components传客户端组件
问题根源
报错的核心原因是Server Action(createUserAction)返回了Response类实例,Next.js不允许从Server Component/Server Action向Client Component传递类实例或非纯plain对象。虽然用户创建操作已成功执行(数据库插入完成),但返回的Response对象在客户端渲染时触发了类型检查错误。
修复步骤
1. 修改Server Action,避免返回类实例
修改actions.ts中的createUserAction,不要直接返回createUser的结果(即Response对象),改为处理响应后返回纯对象,或直接不返回值:
'use server' import { createUser, deleteUser } from '@/api'; import { revalidatePath } from "next/cache"; export async function createUserAction(formData: FormData) { const { name, email, age } = Object.fromEntries(formData); revalidatePath('/admin'); // 处理fetch响应,不返回Response实例 const response = await createUser(name as string, email as string, age as string); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || '创建用户失败'); } // 返回纯对象,或直接省略return return await response.json(); } export async function deleteUserAction(id: number) { revalidatePath('/admin') await deleteUser(id); }
2. 优化api.ts中的createUser函数(推荐)
让createUser直接返回解析后的JSON数据,而非原始Response,简化Server Action代码:
export const BASE_URL = 'http://localhost:3000'; export async function createUser(name: string, email: string, age: string) { const response = await fetch(BASE_URL + '/api/create-user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, // 需添加该Header,否则后端无法解析JSON body: JSON.stringify({ name, email, age }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.error); } return response.json(); }
3. 确保API返回纯对象(可选)
在api/create-user/route.ts中,将SQL查询结果转换为纯对象数组,避免Vercel Postgres返回的结果带有额外属性:
import { sql } from '@vercel/postgres'; import { NextResponse } from 'next/server'; export async function POST(request: Request) { const { name, email, age } = await request.json(); try { if (!name || !email || !age) throw new Error('请填写所有字段'); await sql`INSERT INTO users (name, email, age) VALUES (${name}, ${email}, ${age});`; } catch (error) { return NextResponse.json({ error: (error as Error).message }, { status: 500 }); } const { rows: users } = await sql`SELECT * FROM users;`; // 取rows字段,得到纯用户数组 return NextResponse.json({ users }, { status: 200 }); }
4. 客户端组件添加错误处理(可选)
在AddUser.tsx中,利用useActionState捕获Server Action抛出的错误,提升用户体验:
'use client' import { createUserAction } from '@/app/actions.ts'; import React, { useActionState } from 'react'; export default function AddUser() { const [open, setOpen] = React.useState(false); const [error, createUserWithState] = useActionState(createUserAction, undefined); function handleOpen() { setOpen(true); } return ( <div> <button onClick={handleOpen} className='bg-slate-400 p-3 rounded-xl mb-10 mt-[200px]'>Add new user</button> {open && ( <form action={(formData) => { createUserWithState(formData); setOpen(false); // 提交后关闭表单 }} className="translate-x-[-50%] left-[50%] rounded-xl h-[400px] flex flex-col gap-4 w-[500px] text-black absolute bg-slate-700 p-10 py-20" > {error && <p className="text-red-400">{error.message}</p>} <input type="text" name="name" className='h-[40px] rounded-lg' placeholder="姓名"/> <input type="email" name="email" className='h-[40px] rounded-lg' placeholder="邮箱"/> <input type="number" name="age" className='h-[40px] rounded-lg' placeholder="年龄"/> <button type="submit" className="bg-red-500 text-white h-[40px] rounded-lg mt-auto"> 创建用户 </button> </form> )} </div> ) }
为什么之前的尝试无效?
你之前将AddUser移到Admin页面并移除"use client",但Admin本身是Server Component,表单调用Server Action时,返回的Response实例仍会被Next.js尝试传递,导致相同错误。核心问题始终是Server Action返回了非纯对象。
内容的提问来源于stack exchange,提问作者Ucha
相关产品推荐
相关产品推荐

