Module 5: Dynamic Data & Paths

Dynamic routes සඳහා පිටු pre-render කරන්නේ කෙසේදැයි සොයා බලමු.

ගැටලුව: Dynamic Routes සහ `getStaticProps`

Module 4 හිදී, අපි `getStaticProps` ගැන ඉගෙන ගත්තා. එය build time එකේදී run වී, data fetch කරගෙන, page එකක් HTML file එකක් ලෙස pre-render කරනවා. `pages/posts/index.js` වගේ static path එකක් සඳහා මෙය හොඳින් ක්‍රියාත්මක වෙනවා.

නමුත්, `pages/posts/[id].js` වැනි dynamic route එකක් ගැන සිතන්න. මෙහි `[id]` කොටස ඕනෑම අගයක් ගත හැකියි (`/posts/1`, `/posts/2`, `/posts/abc`, etc.). Build time එකේදී, Next.js දන්නේ කොහොමද `id` එකට අදාළව මොන මොන pages ද generate කරන්න ඕන කියලා? `/posts/1` page එක generate කරන්න ඕනද? `/posts/2` එකත් ඕනද? එසේ නම්, කොපමණ pages generate කරන්න ඕනද?

මෙම ගැටලුව විසඳීම සඳහා, Next.js අපිට තවත් විශේෂ function එකක් ලබා දෙනවා. ඒ තමයි `getStaticPaths`.

1. `getStaticPaths` для Dynamic Routes

getStaticPaths function එක භාවිතා කරන්නේ dynamic page එකක් සමග `getStaticProps` භාවිතා කරන විට පමණි. මෙම function එකේ කාර්යය වන්නේ, build time එකේදී pre-render කළ යුතු සියලුම dynamic paths (`id` වල අගයන්) මොනවාදැයි Next.js වෙත දැනුම් දීමයි.

සරලවම, ඔබ Next.js ට කියනවා, "හරි, `/posts/[id]` කියන route එකට අදාළව, `id` එක `1` වන page එකත්, `2` වන page එකත්, `3` වන page එකත් HTML විදියට generate කරලා තියන්න" කියලා.

භාවිතා කරන ආකාරය:

`getStaticPaths` function එක ඇතුළෙන්, අපි `paths` සහ `fallback` කියන keys දෙක සහිත object එකක් return කළ යුතුයි.

// File: pages/posts/[id].js

export async function getStaticPaths() {
  // Fetch a list of all possible post IDs
  const res = await fetch('https://.../posts');
  const posts = await res.json();

  // Create an array of path objects
  // [{ params: { id: '1' } }, { params: { id: '2' } }, ...]
  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  // We'll pre-render only these paths at build time.
  return { paths, fallback: false };
}

// This also runs at build time for each path returned by getStaticPaths
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();

  // Pass post data to the page via props
  return { props: { post } };
}

// Your page component
export default function Post({ post }) {
  // Render post...
}

ක්‍රියාවලිය:

  1. Build time එකේදී, Next.js මුලින්ම `getStaticPaths` run කරයි.
  2. එයින් return වන `paths` array එකේ ඇති සෑම `params` object එකක් සඳහාම, Next.js විසින් `getStaticProps` function එක run කරයි. (උදා: මුලින්ම `params: { id: '1' }` සමග ද, පසුව `params: { id: '2' }` සමග ද run කරයි).
  3. එක් එක් `getStaticProps` call එකෙන් ලැබෙන props භාවිතා කර, අදාළ HTML page එක (`/posts/1.html`, `/posts/2.html`) generate කර තබයි.

`fallback` යනු කුමක්ද?

`getStaticPaths` වලින් return කරන object එකේ ඇති `fallback` property එක ඉතා වැදගත්.

  • `fallback: false`: මෙයින් කියවෙන්නේ, `getStaticPaths` වලින් return නොකළ path එකකට user කෙනෙක් පිවිසුණහොත්, 404 (Not Found) page එක පෙන්වන ලෙසයි. උදාහරණයක් ලෙස, build time එකේදී post 100ක් generate කර, user කෙනෙක් `/posts/101` වෙත පිවිසුණහොත්, 404 page එක පෙන්වයි. කුඩා සහ දත්ත වෙනස් නොවන sites සඳහා මෙය සුදුසුයි.
  • `fallback: true`: මෙයින් කියවෙන්නේ, `getStaticPaths` වලින් return නොකළ path එකකට user කෙනෙක් පිවිසුණහොත්, 404 page එක පෙන්වන්නේ නැතිව, browser එකට "fallback" version එකක් (loading indicator එකක් වැනි) පෙන්වා, background එකෙන් Next.js විසින් අදාළ HTML සහ JSON එක generate කරන ලෙසයි. එම ක්‍රියාවලිය අවසන් වූ පසු, generate වූ page එක userට පෙන්වයි. ඊළඟට එම path එකට එන userට, generate වූ page එක කෙලින්ම serve කරයි. E-commerce site එකක් වැනි විශාල, දහස් ගණනක් pages ඇති sites සඳහා මෙය සුදුසුයි.
  • `fallback: 'blocking'`: මෙය `fallback: true` ට සමානයි. නමුත් "fallback" version එකක් (loading state) පෙන්වන්නේ නැතුව, server එකේදී HTML එක generate වන තෙක් user request එක "block" කර තබා ගනී. SSR වලට සමාන අත්දැකීමක් ලබා දේ.

2. ISR (Incremental Static Regeneration)

අපි `getStaticProps` භාවිතා කර page එකක් build time එකේදී generate කළාට පස්සේ, API එකේ data update උනොත් මොකද වෙන්නේ? සාමාන්‍යයෙන් නම්, අපි නැවත app එක build කරනකම් userට පේන්නේ පරණ data.

Incremental Static Regeneration (ISR) මගින් මේ ගැටලුවට විසඳුමක් දෙනවා. `getStaticProps` function එකෙන් `props` return කරන විට, `revalidate` නමින් property එකක් එකතු කිරීමෙන් අපිට ISR activate කරන්න පුළුවන්.

export async function getStaticProps() {
  const res = await fetch('https://.../data');
  const data = await res.json();

  return {
    props: {
      data,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every 60 seconds
    revalidate: 60, // In seconds
  };
}

මෙහි `revalidate: 60` යන්නෙන් අදහස් වන්නේ:

  1. Page එකට request එකක් ආ විට, දැනට cache වී ඇති (build time එකේදී හැදූ) page එක userට පෙන්වයි.
  2. අවසන් වරට page එක generate කර තත්පර 60ක් ගත වී ඇත්නම්, Next.js background එකෙන් page එක re-generate කිරීමේ ක්‍රියාවලිය ආරම්භ කරයි.
  3. Background එකේදී, එය නැවත `getStaticProps` run කර, අලුත් data fetch කර, page එකේ නව version එකක් generate කර cache එක update කරයි.
  4. ඊළඟ request එකේ සිට, usersලාට පෙනෙන්නේ මෙම අලුතින් generate කළ page එකයි.

ISR මගින් අපිට SSG හි වේගය සහ SSR හි නිතර යාවත්කාලීන වන දත්ත යන වාසි දෙකම එකවර ලබාගැනීමට ඉඩ සලසයි.

3. Hands-on: Dynamic Blog Article Pages නිර්මාණය

දැන් අපි Module 4 හිදී fetch කරගත් post list එකේ, එක් එක් post එක click කළ විට ඒ post එකට අදාළ dynamic page එකක් පෙන්ව도록 අපේ app එක හදමු.

  1. Post List Page එක Update කිරීම:

    `pages/posts/index.js` file එකට ගොස්, post title එක `Link` component එකක් තුළට දමන්න.

    // File: pages/posts/index.js
    import Link from 'next/link';
    
    // ... (component code remains the same, just update the list item)
    
    <li key={post.id}>
      <Link href={`/posts/${post.id}`}>
        <h3>{post.title}</h3>
      </Link>
    </li>
    
    // ... (getStaticProps remains the same)
    
  2. Dynamic Page එක සෑදීම:

    `pages/posts/` directory එක තුළ `[id].js` නමින් අලුත් file එකක් සාදන්න. (File path: `pages/posts/[id].js`).

    පහත code එක එම file එකට copy කර paste කරන්න.

    // File: pages/posts/[id].js
    import Link from 'next/link';
    
    export default function PostPage({ post }) {
      // If fallback is true, the page might not be generated yet
      if (!post) {
        return <div>Loading...</div>;
      }
      
      return (
        <div>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
          <br />
          <Link href="/posts">&larr; Back to all posts</Link>
        </div>
      );
    }
    
    export async function getStaticPaths() {
      // We'll pre-render the first 10 posts
      const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
      const posts = await res.json();
    
      const paths = posts.map(post => ({
        params: { id: post.id.toString() },
      }));
    
      return {
        paths,
        fallback: false, // For now, paths not returned will result in 404
      };
    }
    
    export async function getStaticProps({ params }) {
      const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`);
      const post = await res.json();
    
      if (!post.id) {
        return {
          notFound: true,
        }
      }
    
      return {
        props: {
          post,
        },
        revalidate: 60, // Re-generate this page every 60 seconds
      };
    }
    
  3. පරීක්ෂා කිරීම:

    දැන් http://localhost:3000/posts වෙත යන්න. Post title එකක් click කළ විට, ඔබ අදාළ post එකේ සම්පූර්ණ content එක ඇති dynamic page එකට (`/posts/1`, `/posts/2` වැනි) navigate වනු ඇත. අපි `fallback: false` ලෙස සැකසූ නිසා, `/posts/11` වැනි path එකකට ගියොත්, ඔබට 404 page එක පෙනෙනු ඇත.