如何在 Next.js 中使用 useActionState 更新和显示分页表中的服务器端数据?

问题描述 投票:0回答:1

我正在使用

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);
  }
}
next.js server-side-rendering
1个回答
0
投票

您必须在更新操作之后和在服务器操作内返回数据之前添加 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);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.