我正在尝试将状态从客户端组件提升到服务器组件,在服务器上执行一些操作,然后通过 props 将状态从服务器传回到客户端。
我正在使用 Next 14、Shadcn-ui、React-hook-form 等创建一个预订系统,这意味着我的表单必须位于客户端组件中,并且我所有的数据获取都位于服务器组件中。
我想要做的是让用户使用日期选择器选择一天,将其传递给服务器,服务器获取当天的预订并计算哪些时间段是空闲的,然后将这些时间段传递回客户端以填充组合框。
我最接近的是状态上升,但我只能得到一个 Promise 回来。这就是我的代码的样子。
服务器组件(父组件)
export type GetAvailableSlotsType = (date: Date) => Slot[];
export default async function NewBookingsPage() {
const getAvailableSlots: GetAvailableSlotsType = async (date) => {
"use server";
console.log(`Date: ${date}`) // Shows correct date
// Do some logic
return availableSlots
}
return (
<NewBookingForm getAvailableSlots={getAvailableSlots} />
)
}
客户端组件(子)
import { GetAvailableSlotsType } from "../page";
type NewBookingFormProps = {
getAvailableSlots: GetAvailableSlotsType;
};
export default function NewBookingForm({
getAvailableSlots,
}: NewBookingFormProps) {
// Some form schema stuff
const availableSlots = getAvailableSlots(form.watch("date"));
return (
<form>
// render form
</form>
)
就像我说的,子组件中的 availableSlots 作为承诺实现并且不可用,尽管从 getAvailableSlots 返回一个很好的可用对象形状。
提前致谢。
服务器组件是静态无状态组件,可以视为纯函数,旨在生成无需 JavaScript 的 UI。子组件不能按照您在图表中的步骤 2 中描述的方式影响父服务器组件。
此外,您没有必要将
getAvailableSlots
服务器操作传递给子组件,您可以将其单独放在一个文件中(请参阅 Next 文档中的 客户端组件服务器操作约定)并直接在任何组件中使用它您希望将其视为异步函数(请参阅 Next 文档中的非表单元素中使用的服务器操作)。
但是,在您的情况下,由于需要传递日期,该模式将需要传递 formData ,建议使用
useTransition
来具有挂起状态。
当您正在设计具有异步数据获取的交互式组件(组合框)时,您不应将服务器组件用于此目的。例如,如果您要根据当前 URL 显示可用性的静态列表,则服务器组件将完美适合此用例。
综上所述,您可以将图中的“服务器组件”替换为“服务器操作”,这是正确的。从 Next13 到 Next14 的重大思维转变是服务器操作不需要服务器组件,因此可以随意在服务器端组件之外使用它们:)
这是对应该有效的代码的一般重构:
服务器操作
export type GetAvailableSlotsType = (date: Date) => Slot[];
"use server";
export default async function getAvailableSlots(date: Date): GetAvailableSlotsType {
console.log(`Date: ${date}`) // Shows correct date
// Do some logic
return availableSlots
}
客户端组件
import { useEffect, useState, useTransition } from "react";
export default function NewBookingForm() {
// Some form schema stuff
const [isPending, startTransition] = useTransition();
const [availableSlots, setAvailableSlots] = useState([])
const date = form.watch("date")
useEffect(() => {
startTransition(async () => {
const slots = await getAvailableSlots(date);
setAvailableSlots(slots);
})
}, [date]);
const availableSlots = getAvailableSlots(form.watch("date"));
return (
<form>
{/* render form */}
{/* render combobox somewhere, you can render a loader with isPending inside the combobox */}
<ComboBox choices={availableSlots} isPending={isPending} />
</form>
)
如果您还有任何疑问,请随时询问。
祝您项目成功!