Published on

Pierwszy projekt w Next.js 13. dynamiczne strony (SSR) generowane na serwerze z plików CSV

Authors

Next.js - Pierwsza statyczna strona na Next.js a tak naprawdę React ze sterydami.

Dlaczego wybory2023.pl?

Historia rozpoczęła się w 2020 roku, w czasie pandemii COVID-19, gdy w Polsce odbyły się wybory prezydenckie. Wtedy pojawił się pomysł stworzenia prostej, wiralowej gry strategicznej lub klikanej, osadzonej w polskiej rzeczywistości politycznej. Plan zakładał, że gra mogłaby zyskać popularność na platformach takich jak MLH lub Wykop, co pozwoliłoby sprawdzić działanie serwisu i przyniosłoby trochę zabawy.

Kiedy okazało się, że domena wybory2023.pl jest wolna, natychmiast ją zakupiłem. Mineło trochę czasu... Niestety, przypomniałem sobie o byćiu dumnym posiadaczem domeny wybory2023.pl zbyt późno, dopiero we wrześniu 2023 roku. Niecały miesiąc przed wyborami do sejmu i senatu. Brakowało mi inspiracji co do szczegółów i motywacji do dalszego działania, więc postanowiłem zrezygnować z pomysłu gry politycznej. Nie chciałem pozostawić domeny kompletnie nieużywanej, więc w ramach minimalnego wysiłku, mając świadomość, że projekt nie ma szans na sukces, zdecydowałem się umieścić na stronie przynajmniej sondy i wyniki wyborcze.

To był dobry moment żeby sprawdzić jak działa Next.js, który idealnie nadaje się do generowania i cachowania statycznych stron. Jak to się mówi, nie ma nic szybszego niż statyczny HTML na Nginx :) a później tylko load-balancer i wincyj serwerów.

Za źródło danych posłużyła mi oficjalna strona PKW (https://wybory.gov.pl/sejmsenat2023/pl/dane_w_arkuszach) i arkusze z wynikami w postaci plików CSV. Arkusze były wczytywane i przerabiane na stronę www która renderowała się na serwerze. Zrobienie samej strony zajeło mi kilka-kilkanaście godzin. Poniżej linki do projektu:

Next.js, co to jest?

Next.js to framework JavaScript, oparty na React. Pozwala generować statyczne strony na serwerze. Statyczne pliki HTML to oczywiście większa wydajność, szybkość ładowania strony i lepsze SEO. To wszystko było już wcześniej ale Next.js czyni to po prostu wygodnym.

W projekcie wykorzystałem wersję 13 - to ma znaczenie bo wersja ta wprowadza kilka istotnych zmian. Najważniejszą zmianą która będzie rzutować również na Twoim nowym projekcie Next.js jest wykorzystanie katalogu app zamiast pages. Niby niewielka zmiana a różnica w użyciu jest spora. Zmienia się routing oraz przekazywanie props'ów dla dynamicznych stron.

Katalog app został wprowadzony w wersji 13, więcej o zmianach przeczytasz na oficjalnej stronie Next.js. Stawiając swoją pierwszą stronę bazowałem na dokumentacji i różnych wpisach/poradnikach. Moje zdziwienie i frustracja były coraz większe gdy kolejne proponowane rozwiązania nie działały. Problem był już ciekawy bo chciałem generować na podstawie pliku CSV, strony dla pojedynczych okręgów wyborczych 2023wybory.pl/sejm/{NrOkręgu}.

Generowanie statycznych stron i dynamiczny routing

W poprzedniej architekturze opartej na pages żeby wygenerować statyczne strony dla dynamicznych danych, na przykład: products/{category}/{id} używaliśmy dwóch metod:

  • getStaticProps - pobranie danych dla statycznej strony,
  • getStaticPaths - generacja dynamicznych ścieżek.

Wiele odpowiedzi na stackoverflow oraz poradników opisują stary sposób. Next.js 13 dalej umożliwia korzystanie z katalogu pages jednak preferowany jest app. Ma on więcej funkcjonalności i jest to domyślny katalog który przesłoni routing pages. App jest zoptymalizowany i pozwala na wygenerowanie większości kodu po stronie serwera. Nie zaleca sie stosowania obu katalogów, tworząc nowy projekt wybierz app. Miej to na uwadze gdy przeglądasz blogi i dokumentacje. Dopiero wątek na stackoverflow uświadomił mi że są dwie wersje dokumentacji dla Next.js, a ja przez jakiś czas używałem nie tej co trzeba, zonk 😆.

Dla stron opartych na app powinniśmy użyć generateStaticParams żeby wygenerować dynamiczny routing. Wczytujemy dane w dowolny sposób i przekazujemy do funkcji generateStaticParams która zwraca listę z props'ami dla każdej ze stron. Pamiętaj że musimy eksportować generateStaticParams. Zwracana lista jest iterowana po elementach, na podstawie tego generowane są statyczne strony gdzie do export default function przekazujemy parametry params.

W moim przypadku wczytywałem dane z lokalnego pliku CSV, na podstawie arkusza generowałem stronę dla każdego z okręgów wyborczych. Poniżej kod strony:

// file: app/districts/[id].tsx
import { getCsvDistrictIds, getSejmCsvDataByDistrict, getSejmCandidatesCsvDataByDistrict } from '@/lib/getCsvData';

interface PageProps {
  params: {
    id: number;
    data?: any;
  };
}

export async function generateStaticParams() {
  const paths = getCsvDistrictIds('sejm.csv');
  return paths.map((id) => {
    return {
      params: {
        id: id,
      },
    };
  });
}

export default function Page({ params }: PageProps) {
  const district = getSejmCsvDataByDistrict(params.id).region;
  const candidates = getSejmCandidatesCsvDataByDistrict(params.id).candidates;

  return (
    <div className='container mx-auto max-w-screen-xl px-4 pb-6 sm:px-6 lg:px-8'>
      <h1 className="text-2xl font-bold">Okręg: {params.id}</h1>
      {JSON.stringify(district)}
      {JSON.stringify(candidates)}
    </div>
  );
}

Jeśli Cię zainteresowałem możesz zajrzeć do repozytorium i na oficjalną stronę Next.js. Projekt bardzo zyskuje na popularności i wydaję się istotnym rozszerzeniem dla ekosystemu React. Jest to doby wybór szczególnie dla dużych serwisów, blogów, i stron które mocno stawiają na SEO i wydajność. Next.js ma solidny trend wzrostowy i nic nie zapowiada żeby był mniej popularny. Osobiście jestem bardzo zadowolony, uważam że w końcu powstało dobre rozwiązanie dla obsługi React na serwerze. Jest to bardzo kompleksowe rozwiązanie zaakceptowane przez community z dużym wsparciem innych rozwiązań/twórców.