我正在使用
useActionState
触发服务器端操作 fetchPeople
来提交表单。在表单之外,有一个带分页的表格,我在其中显示从服务器返回的数据。
我正在尝试通过传递额外的 prop(页面)来更新 serverState,以再次触发 fetch API、更改当前页面并从该页面返回新人员。
通过单击表格下方分页中的新页面,我已成功完成此操作...我可以看到响应中的分页和人员对象已更改,但我在向客户端发送更新后的响应时遇到问题。
我在哪里犯了错误,导致我无法将更新后的数据发送到客户端并将其显示在表格中?
StepOneForm.tsx
"use client"
export default function StepOneForm() {
const [serverState, formAction, isPending] = useActionState(fetchPeople, {});
const formRef = useRef<HTMLFormElement | null>(null);
const totalPages = serverState.pagination?.total_pages ?? 0;
const currentPage = serverState.pagination?.page || 1;
const totalEntries = serverState.pagination?.total_entries;
const pagination = getPages({ totalPages, currentPage });
async function handlePageChange(page: number) {
if (formRef.current) {
const formData = new FormData(formRef.current);
fetchPeople(serverState, formData, page);
}
}
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
const formData = new FormData(event.target as HTMLFormElement);
startTransition(() => {
formAction(formData);
});
};
return (
<>
<div className="grid grid-row sm:grid-cols-2 gap-5 mt-7">
<form
onSubmit={handleSubmit}
ref={formRef}
className="order-2 sm:order-1"
>
<!-- not relevant code -->
</form>
<!-- table with data returned from server side action -->
<div className="grid grid-cols-1 md:grid-cols-3 mt-3">
{serverState.people.map((contact: any) => {
return (
...
);
})}
</div>
<!-- table pagination -->
<div className="flex items-center justify-between border-t border-gray-200 bg-white py-3">
<nav
aria-label="Pagination"
className="isolate inline-flex -space-x-px rounded-md shadow-sm"
>
{pagination.map((page, index) =>
page === "..." ? (
<span
key={`ellipsis-${index}`}
className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-500"
>
...
</span>
) : (
<Link
href=""
key={page}
className={`cursor-pointer relative inline-flex items-center px-4 py-2 text-sm font-semibold ${
page === currentPage
? "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0"
}`}
onClick={async (e) => {
e.preventDefault();
await handlePageChange(+page);
}}
>
{page}
</Link>
)
)}
</nav>
</div>
</div>
行动/people.ts
"use server"
export async function fetchPeople(
prevState: any,
formData: FormData,
page: number = 1
): Promise<any> {
try {
const validated = stepOneSchema.safeParse({
name: formData.get("name"),
company: formData.get("company"),
web: formData.get("web"),
country: formData.get("country"),
});
if (!validated.success) {
return {
errors: validated.error.flatten().fieldErrors,
};
}
const baseUrl = `https://api.apollo.io/api/v1/mixed_people/search`;
const params = new URLSearchParams();
const { name, company, web, country } = validated.data;
if (country) params.append("organization_locations[]", country);
if (name) params.append("q_keywords", name);
if (company) params.append("q_keywords", company);
if (web) params.append("q_organization_domains[]", web);
const url = `${baseUrl}?${params.toString()}&page=${page}&per_page=10`;
const response = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"Cache-Control": "no-cache",
"Content-Type": "application/json",
"x-api-key": process.env.APOLLO_API_KEY as string,
},
});
const peopleData = await response.json();
if (!response.ok) {
throw new Error(`Apollo API error: ${peopleData.message}`);
}
const people = peopleData.people.map((person: any) => ({
name: person.name,
title: person.title,
company: person.organization.name,
}));
const pagination = peopleData.pagination;
console.log('server side response', pagination, people);
return { success: true, people, pagination };
} catch (error: any) {
console.error(error);
}
}
您必须在更新操作之后和在服务器操作内返回数据之前添加 revalidatePath,如下所示
"use server"
export async function fetchPeople(
prevState: any,
formData: FormData,
page: number = 1
): Promise<any> {
try {
const validated = stepOneSchema.safeParse({
name: formData.get("name"),
company: formData.get("company"),
web: formData.get("web"),
country: formData.get("country"),
});
if (!validated.success) {
return {
errors: validated.error.flatten().fieldErrors,
};
}
const baseUrl = `https://api.apollo.io/api/v1/mixed_people/search`;
const params = new URLSearchParams();
const { name, company, web, country } = validated.data;
if (country) params.append("organization_locations[]", country);
if (name) params.append("q_keywords", name);
if (company) params.append("q_keywords", company);
if (web) params.append("q_organization_domains[]", web);
const url = `${baseUrl}?${params.toString()}&page=${page}&per_page=10`;
const response = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"Cache-Control": "no-cache",
"Content-Type": "application/json",
"x-api-key": process.env.APOLLO_API_KEY as string,
},
});
const peopleData = await response.json();
if (!response.ok) {
throw new Error(`Apollo API error: ${peopleData.message}`);
}
const people = peopleData.people.map((person: any) => ({
name: person.name,
title: person.title,
company: person.organization.name,
}));
const pagination = peopleData.pagination;
console.log('server side response', pagination, people);
revalidatePath('/your-path')//this line
return { success: true, people, pagination };
} catch (error: any) {
console.error(error);
}
}