Bart
Bart

Reputation: 339

Getting undefined params in my compoment with generateStaticParams nextjs 13. How to pass them correctly?

I'm using the App router of nextjs 13, with typescript, and I'm trying to create dynamic pages and generate the paths for them with generateStaticParams().

The function generateStaticParams() seems to work, and it collects the correct information. But when I want to use the information in my component, I get a undefined value.

The page is fetchting the data correctly if I hardcode the ID, so I only need to swap this for the ID collected with the generateStaticParams.

How can I make sure that the page is receiving the data from generateStaticParams?

/src/app/coffeeplaces/[coffeeplace]/page.tsx
import fetchCoffeePlace from "@/api/fetchCoffeeplace";
import { db } from "@/helpers/firebaseConfig";
import { collection, getDocs } from "firebase/firestore";
import Image from "next/image";
import { Suspense } from "react";
import ImagesCarousel from "./components/imagesCarousel";
import Link from "next/link";
import CoffeeMainDetails from "./components/coffeeMainDetails";

export default async function Page({ params }: { params: { id: string, slug: string } }) {
  const { id, slug } = params

  // this comes back UNDEFINED
  console.log('id', params.id) // or id
  console.log('slug', params.slug) // or slug

  // This hardcoded option works to fetch the data
  // const id = 'r7CFv3t4Ni5sNl20wE8h'

  const CoffeeplaceData = fetchCoffeePlace(id);
  const CoffeeplaceInfoData = fetchCoffeePlace(id); // 

  const coffeeplace = await CoffeeplaceData;

  return (
    <div>
      <Link href={'/coffeeplaces'} className="text-sm font-light">Back to listing</Link>
      <Suspense fallback={<div>Loading...</div>}>
        <CoffeeplaceInfo promise={CoffeeplaceInfoData} />
      </Suspense>
    </div>
  );
}

async function CoffeeplaceInfo({ promise, }: { promise: Promise<CoffeePlace>; }) {
  const coffeeplaceInfo = await promise;
  return (
      <>
      <div className="pt-6 lg:flex w-full">
        <ImagesCarousel featuredImage={coffeeplaceInfo.featuredImage} />
        <CoffeeMainDetails name={coffeeplaceInfo.name} priceRange={coffeeplaceInfo.priceRange} organic={coffeeplaceInfo.organic} fairTrade={coffeeplaceInfo.fairTrade}/>
      </div>
      </>
    )
}

export async function generateStaticParams() { // tried with this: generateStaticParams({ params: { id, slug }}: any)
  const coffeePlaces = await getDocs(collection(db, "coffeePlaces"));
  const data = coffeePlaces.docs.map((doc) => ({
    id: doc.id,
    ...doc.data()
  }));

  var slugify = require('slugify')

  const result = data.map((item) => {
    const slug = slugify(item.name, {
      replacement: '-',  
      lower: true,  
      strict: true,
    });

    // these are logging the correct values
    console.log(item.id);
    console.log(slug);
    
    return {
      params: {
        id: item.id,
        slug: slug
      }
    };
  }); 

  // this is logging the correct result
  // [{ params: { id: '9ZVOCYsngTksBKXqQQOH', slug: 'coffeeplace-1' } }, { params: { id: 'r7CFv3t4Ni5sNl20wE8h', slug: 'example-2' } } ]

  return result; 
}

Using parallel Data Fetching

Small other thing, might be related, in the generateStaticParams function, when I loop over the result, for the item.name it says: Property 'name' does not exist on type '{ id: string; }')

Edit: Trying out with new environment

I've created a new nextjs project to test this out, and got the same results.

  1. I installed Nextjs 13 app router, typescript and tailwind with npx create-next-app@latest my-project --typescript --eslint
  2. I created a folder structure like so src/app/books/[type]/[book-id]/page.tsx, and used the following example to test it: Example of generateStaticParams

When I log the results I get an unidentified and when I build with npm run build it doesn't show the build of the pages.

Sandbox example

Upvotes: 1

Views: 5604

Answers (3)

roudlek
roudlek

Reputation: 385

you are only allowed to use the params that are in [ ] and nothing else

so your component must have:

  • "use client" at top of component, not page.js (your not allowed to combine clent and server side in page.js)
  • component need params for example: id, and in your component you add the the api call to fetch using component id parameter useEffect useState ...
  • last thing you go to page.js call getStaticParams() (get it from documentation ) return only id as specified, and in Page down call your component and pass it that id that is passed to Page as param

Upvotes: 0

Danila
Danila

Reputation: 18486

You have mismatch between dynamic segment name and what you expect in the code. The naming should be consistent between folder name and what you expect in the code.

You have coffeeplace segment (/src/app/coffeeplaces/[coffeeplace]/page.tsx) in the file system, but for some reason you want to pass id and slug keys from the generateStaticParams function. You can't do that, the key that you are passing from generateStaticParams should also be named coffeeplace and in your case it's the single key, you can't pass more info, other keys etc.

Info from the docs:

generateStaticParams should return an array of objects where each object represents the populated dynamic segments of a single route.

Each property in the object is a dynamic segment to be filled in for the route. The properties name is the segment's name, and the properties value is what that segment should be filled in with.

/product/[id] -> { id: string }[]

/products/[category]/[product] -> { category: string, product: string }[]

/products/[...slug] -> { slug: string[] }[]

More in the docs

Upvotes: 3

Yilmaz
Yilmaz

Reputation: 49303

your result array has this format

result=[params: {id: "id_1",slug: "slug_1"},params: {id: "id_2",slug: "slug_2"},.....]

when you reach params in Page component, you should be using,

const { id, slug } = params.params

because params in each dynamic page is

 params=params: {id: "id_1",slug: "slug_1"}

Upvotes: 0

Related Questions