Dinamičke rute
Upoznati ćemo se sa još jednom funkcionalnošću Next.js okvira, a to je dinamičko usmjeravanje (dynamic routing) tj. navigacija. U prošlom primjeru smo implementirali dohvat podataka za prvih 20 Pokemona. Sada želimo za svakog Pokemona napraviti njegovu vlastitu stranicu, te naravno da do te stranice možemo pristupiti preko URL-a koji izgleda otprilike ovako: app/pokemon/imePokemona. Naravno da nema smisla ručno raditi pojedinačnu stranicu za svakog pojedinog Pokemona...
Srećom, tu nam pomaže Next.js i mogućnost definiranja dinamičkih ruta. Postupak je jednostavan:
- Unutar
app/stvaramo direktorijpokemon/. - U
pokemon/direktoriju ćemo napraviti dinamički direktorij[name]/(zagrade označavaju da se radi o dinamičkom imenu). - U
[name]/stvaramo novu datotekupage.js.
Konačna struktura izgleda ovako:
app/
├─ layout.js
├─ page.js // Početna stranica (popis Pokémona)
└─ pokemon/
└─ [name]/
└─ page.js // Detalji pojedinog PokémonaDohvat podataka
Sada možemo implementirati stranicu za prikaz detalja jednog Pokemona. Pogledajmo prvo gotov primjer pa ćemo se osvrnuti na ključne dijelove:
export default async function PokemonDetalji({ params }) {
const { name } = await params; // Dinamički parametar iz rute, npr. /pokemon/bulbasaur
// Dohvat podataka s API-ja na temelju imena Pokémona
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
// Ako Pokémon ne postoji
if (!res.ok) {
throw new Error('Pokémon nije pronađen!');
}
const data = await res.json();
// Prikazujemo osnovne podatke
return (
<main>
<h1>{data.name}</h1>
<p>ID: {data.id}</p>
<p>Težina: {data.weight}</p>
<p>Visina: {data.height}</p>
</main>
);
}Prođimo sada malo detaljnije kroj pojedine dijelove kôda.
-
params objekt (koji se naravno nalazi unutar props-a) u sebi sadrži varijable (dinamičke vrijednost) iz rute. Ako smo dinamički direktorij nazvali "[name]", unutar params objekta možemo pristupiti svojstvu params.name i dobiti vrijednost koju je korisnik upisao kao URL stranice.
-
Dohvat parametara iz rute je asinkrona operacija, pa koristimo await naredbu kako bi bili sigurni da imamo ispravnu vrijednost name parametra prije nego je iskoristimo za slanje zahtjeva
-
Dohvat podataka (fetch) ponovno radimo izravno u tijelu funkcije, jer je riječ o poslužiteljskoj komponenti (SSR). Koristimo parametar iz rute (name) za slanje GET zahtjeva na API.
-
U slučaju nepostojećeg Pokémona (korisnik upiše adresu sa pogrešnim imenom), "bacamo" pogrešku što će uzrokovati "rušenje" aplikacije. Za početak je ovo prihvatljivo jer testiramo aplikaciju ali konačna verzija aplikacije bi svakako trebala sadržavati neku logiku za prikaz prilagođene 404 (not found) stranice tj. stranice koja korisnika obavještava da je upisao pogrešnu adresu i da tražena stranica ne postoji.
-
Nakon što smo dohvatili podatke, ispisujemo osnovne informacije o Pokemonu.
Povezivanje sa početnom stranicom
Dinamičke rute rade ispravno (možemo ih testirati upisivanjem imena bilo kojeg Pokemona) ali nam nedostaju poveznice (linkovi) na glavnoj stranici. Taj dio je jednostavan za implementirati, samo ćemo modificirati način na koji prikazujemo imena Pokemona.
import Link from 'next/link';
export default async function Home() {
const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=20');
const data = await res.json();
const pokemoni = data.results || [];
return (
<main>
<h1>Pokédex Aplikacija</h1>
<ul>
{pokemoni.map((pokemon, index) => {
const ime = pokemon.name;
return (
<li key={index}>
<Link href={`/pokemon/${ime}`}>
{ime}
</Link>
</li>
);
})}
</ul>
</main>
);
}U odnosu na prethodno stanje samo smo malo modificirali logiku prikaza. Umjesto ispisivanja samo imena Pokemona, za svaki element niza (po kojem prolazimo sa map metodom) vraćamo <li> element koji unutar sebe sadrži <Link> komponentu. Radi se o posebnoj Next.js komponenti koja nadograđuje klasičnu HTML <a> oznaku i dodaje joj napredne mogućnosti kao što je automatsko učitavanje podataka (prefetching) i klijentska navigacija između ruta. I dalje se koristi atribut href koji upućuje na dinamičku adresu sa imenom Pokemona.
