Next 14中Server与Client组件双向传态:返回Promise无法正常使用
预订系统中Server Action返回值的异步处理方案
核心问题
你调用Server Action后得到的是Promise,这是因为Server Action本质是异步函数,不能直接把返回值当作同步数组使用,必须通过异步处理逻辑解析Promise并更新组件状态。
解决方案步骤
修正类型定义
Server Action是异步函数,所以GetAvailableSlotsType的返回值类型应该是Promise<Slot[]>,而非直接的Slot[],否则会出现类型不匹配问题。在Client组件中处理异步逻辑
使用useState存储可用时段和加载状态,通过useEffect监听日期选择器的变化,当日期更新时触发Server Action,解析Promise后更新状态。添加加载与错误处理
加入加载状态提示用户,同时捕获请求错误避免页面崩溃。
修改后的完整代码
Server Component(父组件)
export type GetAvailableSlotsType = (date: Date) => Promise<Slot[]>; // 假设Slot类型定义如下 type Slot = { id: string; time: string }; export default async function NewBookingsPage() { const getAvailableSlots: GetAvailableSlotsType = async (date) => { "use server"; console.log(`Date: ${date}`); // 替换为你的实际预订记录查询与可用时段计算逻辑 const availableSlots: Slot[] = [ { id: "slot-1", time: "09:00-10:00" }, { id: "slot-2", time: "10:00-11:00" }, { id: "slot-3", time: "14:00-15:00" } ]; return availableSlots; }; return <NewBookingForm getAvailableSlots={getAvailableSlots} />; }
Client Component(子组件)
"use client"; import { useState, useEffect } from "react"; import { useForm } from "react-hook-form"; import { GetAvailableSlotsType } from "../page"; type Slot = { id: string; time: string }; type FormValues = { date: Date }; type NewBookingFormProps = { getAvailableSlots: GetAvailableSlotsType; }; export default function NewBookingForm({ getAvailableSlots }: NewBookingFormProps) { const { watch, register } = useForm<FormValues>(); const selectedDate = watch("date"); // 存储可用时段和加载状态 const [availableSlots, setAvailableSlots] = useState<Slot[]>([]); const [isLoading, setIsLoading] = useState(false); // 监听日期变化,触发Server Action useEffect(() => { if (!selectedDate) return; const fetchSlots = async () => { setIsLoading(true); try { const slots = await getAvailableSlots(selectedDate); setAvailableSlots(slots); } catch (err) { console.error("获取可用时段失败:", err); setAvailableSlots([]); } finally { setIsLoading(false); } }; fetchSlots(); }, [selectedDate, getAvailableSlots]); return ( <form> <div className="mb-4"> <label htmlFor="date" className="block mb-1">选择预订日期</label> <input type="date" id="date" {...register("date")} className="border rounded px-3 py-2" /> </div> <div className="mb-4"> <label htmlFor="slot" className="block mb-1">选择可用时段</label> {isLoading ? ( <p className="text-sm text-gray-500">加载中...</p> ) : ( <select id="slot" className="border rounded px-3 py-2 w-full" disabled={availableSlots.length === 0} > {availableSlots.length === 0 ? ( <option value="" disabled>暂无可用时段</option> ) : ( availableSlots.map(slot => ( <option key={slot.id} value={slot.id}> {slot.time} </option> )) )} </select> )} </div> {/* 其他表单字段与提交按钮 */} <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">提交预订</button> </form> ); }
关键说明
- 类型修正:确保Server Action的类型正确反映异步特性,避免类型报错。
- 异步触发:通过
useEffect监听日期变化,自动触发时段查询,无需手动点击按钮。 - 用户体验:加载状态和空状态提示让用户清晰了解当前操作进度。
内容的提问来源于stack exchange,提问作者Owain Williams
相关产品推荐
相关产品推荐

