最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Server side component list filtering in Next.js - Stack Overflow

programmeradmin2浏览0评论

I was used to the previous Next.js architecture and I started a new project where I'm trying to use the new architecture with server and client ponents.

I have a page that is a race result table that needs to be rendered on the server for SEO purpose. Once the page is loaded, I want to have a filter input field to allow the user to find the athlete they are looking for.

I'm a bit confused about how to do it, and I feel like it's impossible. I tried to use the getServerSideProps function as in the previous architecture, but that is not working on the new one.

Any idea about how to do it ? Here is what my page code actually looks like:

const RaceResultsPage = async ({ params }: { params: { raceId: string } }) => {
    const result = await getResultByRaceId(+params.raceId);

    return (
        <>
            <div>
                {/* The input I want to use to filter the table*/}
                <div className="relative mt-2 rounded-md shadow-sm">
                    <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
                        <MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
                    </div>
                    <input
                        type="email"
                        name="email"
                        id="email"
                        className="block w-full rounded-md border-0 py-1.5 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                        placeholder="Filtrer par nom, prénom, numéro, club"
                    />
                </div>
            </div>

            <div className="mt-8 flow-root">
                <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
                    <div className="inline-block min-w-full py-2 align-middle">
                        <table className="min-w-full divide-y divide-gray-300">
                            <thead>
                            <tr>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Pos.
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Bib.
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Nom
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Cat
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Temps
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Ecart
                                </th>
                            </tr>
                            </thead>
                            <tbody className="divide-y divide-gray-200 bg-white">
                            {result.map((person) => (
                                <tr key={person.bib}>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.pos}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.bib}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900">
                                        {person.name}
                                    </td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.catRank}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.totalTime}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.diff}</td>
                                </tr>
                            ))}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </>
    );
};

I'm using Next.js 14.

I was used to the previous Next.js architecture and I started a new project where I'm trying to use the new architecture with server and client ponents.

I have a page that is a race result table that needs to be rendered on the server for SEO purpose. Once the page is loaded, I want to have a filter input field to allow the user to find the athlete they are looking for.

I'm a bit confused about how to do it, and I feel like it's impossible. I tried to use the getServerSideProps function as in the previous architecture, but that is not working on the new one.

Any idea about how to do it ? Here is what my page code actually looks like:

const RaceResultsPage = async ({ params }: { params: { raceId: string } }) => {
    const result = await getResultByRaceId(+params.raceId);

    return (
        <>
            <div>
                {/* The input I want to use to filter the table*/}
                <div className="relative mt-2 rounded-md shadow-sm">
                    <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
                        <MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
                    </div>
                    <input
                        type="email"
                        name="email"
                        id="email"
                        className="block w-full rounded-md border-0 py-1.5 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                        placeholder="Filtrer par nom, prénom, numéro, club"
                    />
                </div>
            </div>

            <div className="mt-8 flow-root">
                <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
                    <div className="inline-block min-w-full py-2 align-middle">
                        <table className="min-w-full divide-y divide-gray-300">
                            <thead>
                            <tr>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Pos.
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Bib.
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Nom
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Cat
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Temps
                                </th>
                                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                                    Ecart
                                </th>
                            </tr>
                            </thead>
                            <tbody className="divide-y divide-gray-200 bg-white">
                            {result.map((person) => (
                                <tr key={person.bib}>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.pos}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.bib}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900">
                                        {person.name}
                                    </td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.catRank}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.totalTime}</td>
                                    <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{person.diff}</td>
                                </tr>
                            ))}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </>
    );
};

I'm using Next.js 14.

Share Improve this question edited Feb 11, 2024 at 10:11 Youssouf Oumar 46.6k16 gold badges103 silver badges105 bronze badges asked Feb 9, 2024 at 23:19 Cédric GuérinCédric Guérin 1081 silver badge8 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

If you want your page to be a server ponent, the only way to have a filter is to reload the page with something in the URL, a query string for example.

Still, to update the URL, you would need a client ponent while keeping the page itself as a server ponent.

For example, you can do so:

// app/SetQueryFilters.tsx

"use client";

import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback } from "react";

export default function SetQueryFilters() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();

  const createQueryString = useCallback(
    (name: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString());
      params.set(name, value);

      return params.toString();
    },
    [searchParams]
  );

  return (
    <>
      <input
        type="text"
        value={searchParams.get("filter") || ""}
        onChange={(e) => {
          router.push(pathname + "?" + createQueryString("filter", e.target.value));
        }}
      />
    </>
  );
}
// app/page.tsx

import { Suspense } from "react";
import SetQueryFilters from "./SetQueryFilters";

const RaceResultsPage = async ({
  params,
  searchParams,
}: {
  params: { raceId: string };
  searchParams: { filter: string };
}) => {
  const result = await fetch("https://jsonplaceholder.typicode./users");
  let data = await result.json();
  data = data.filter((person) => {
    if (searchParams.filter) {
      return person.name.toLowerCase().includes(searchParams.filter.toLowerCase());
    }
    return true;
  });
  return (
    <>
      <Suspense fallback={<div>Loading...</div>}>
        <SetQueryFilters />
      </Suspense>
      <table>
        <thead>
          <tr>
            <th scope="col">Name</th>
            <th scope="col">Username</th>
            <th scope="col">Email</th>
          </tr>
        </thead>
        <tbody>
          {data.map((person) => (
            <tr key={person.id}>
              <td>{person.name}</td>
              <td>{person.username}</td>
              <td>{person.email}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  );
};

export default RaceResultsPage;
发布评论

评论列表(0)

  1. 暂无评论