Problem using map to parse getStaticProps

default discord avatar
nball
2 months ago
13

This is less of a Payload question, however I am using Payload 🙌 . When calling Payload using getStaticProps, the fetched data is shaped as an array of objects and arrays. On my standalone Post page, eg.

[slug].tsx

it works fine, however now that I am using a persistent layout function, it seems the map function no longer works with the returned data. I feel like I am missing something obvious here…


// This works on single posts, however does not use persistent layout
// const Posts: React.FC<{ post: Post }> = ({ post }) => {
const Page: NextPageWithLayout = (posts: Post) => {
  return (
    <>
      <section>
        <div className='wrap'>
          <h1>Blog</h1>
          {/* {posts.map((post) => (
            <BlogPostCard
              key={post.id}
              slug={post.slug}
              title={post.title}
              excerpt={post.excerpt}
              displayDate={post.displayDate}
            />
          ))} */}
        </div>
      </section>
    </>
  );
};

export const getStaticProps: GetStaticProps<any> = async (context) => {
  const postQuery = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/posts`).then((res) =>
    res.json(),
  );
  console.log(postQuery.docs);
  return {
    props: {
      posts: postQuery.docs,
    },
  };
};

Page.getLayout = function getLayout(page: ReactElement) {
  return <Layout>{page}</Layout>;
};


console.log(postQuery.docs);

yields

 [
  {
    id: '64bb178a958a467d807ccf76',
    title: 'Why Commercial Real Estate is Better Than Single Family',
    featuredImage: {
      id: '64bb1786958a467d807ccf72',
      title: 'Office buildings waterfront',
      alt: 'Office buildings waterfront',
      filename: 'office-buildings-waterfront.jpg',
      mimeType: 'image/jpeg',
      filesize: 109484,
      width: 960,
      height: 641,
      sizes: [Object],
      createdAt: '2023-07-21T23:40:54.100Z',
      updatedAt: '2023-07-21T23:40:54.100Z',
      url: '/media/office-buildings-waterfront.jpg'
    },

etc, where each retrieved entry is an object within the array.

  • default discord avatar
    baotaoh
    2 months ago

    Thats a typeerror , adjust the type of posts to be array.



    // This works on single posts, however does not use persistent layout
    // const Posts: React.FC<{ post: Post[] }> = ({ post }) => {
    const Page: NextPageWithLayout = (posts: Post[]) => {
      return (
        <>
          <section>
            <div className='wrap'>
              <h1>Blog</h1>
               {posts.map((post) => (
                <BlogPostCard
                  key={post.id}
                  slug={post.slug}
                  title={post.title}
                  excerpt={post.excerpt}
                  displayDate={post.displayDate}
                />
              ))} 
            </div>
          </section>
        </>
      );
    };
    
    export const getStaticProps: GetStaticProps<any> = async (context) => {
      const postQuery = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/posts`).then((res) =>
        res.json(),
      );
      console.log(postQuery.docs);
      return {
        props: {
          posts: postQuery.docs,
        },
      };
    };
    
    Page.getLayout = function getLayout(page: ReactElement) {
      return <Layout>{page}</Layout>;
    };
  • default discord avatar
    nball
    2 months ago

    @baotaoh I thought this may be the case, however the map error remains.

    error Error [TypeError]: posts.map is not a function

    which is odd, as VS Code no longer shows lint errors in the UI.



    With this change

    const Page: NextPageWithLayout = (posts: Post[]) => {

    the console log

    console.log(posts);

    yields

    {
      posts: [
        {
          id: '64bb178a958a467d807ccf76',
          title: 'Why Commercial Real Estate is Better Than Single Family',
          featuredImage: [Object],

    etc



    My lack of basic skills is shining through

  • default discord avatar
    baotaoh
    2 months ago

    that seems correct, you should be able to call the map method since post is an array.


    are you getting an error in the browser?

  • default discord avatar
    nball
    2 months ago

    The error is in browser. Unhandled Runtime Error


    TypeError: posts.map is not a function

  • default discord avatar
    baotaoh
    2 months ago

    that means the page is not receiving the data, are you sure your request is being resolved? add some error handling.


    and do

    posts?.map

    instead

  • default discord avatar
    nball
    2 months ago

    The fetch works based on console.log, so I don't think it's that. Adding the optional ? doesn't change anything.

    TypeError: posts?.map is not a function
  • default discord avatar
    baotaoh
    2 months ago

    Oh, i see it now.

    posts

    in this case is an alias for

    props


    you can either desctruture i.e

    const Comp = ({posts}) =>{...}

    or

    const Comp = (posts) =>{ console.log(posts.posts) }


    but i will recommend the first one or renaming to

    props
  • default discord avatar
    nball
    2 months ago

    Here's what worked based on your advice, note the array check. That's the significant change.

    const Page: NextPageWithLayout = (posts: Post[]) => {
      return (
        <>
          <Head>
            <title>Blog</title>
          <section>
            <div className='wrap'>
              <h1>Blog</h1>
              {posts &&
                Array.isArray(posts) &&
                posts.length > 0 &&
                posts.map((post) => (
                  <BlogPostCard
                    key={post.id}
                    slug={post.slug}
                    title={post.title}
                    excerpt={post.excerpt}
                    displayDate={post.displayDate}
                  />
                ))}
            </div>
          </section>
        </>
      );
    };
    
    export const getStaticProps: GetStaticProps<any> = async () => {
      const postQuery = await fetch(
        `${process.env.NEXT_PUBLIC_CMS_URL}/api/posts?where[_status][equals]=published&depth=0&limit=300`,
      ).then((res) => res.json());
    
      return {
        props: {
          posts: postQuery.docs,
        },
      };
    };
    
    Page.getLayout = function getLayout(page: ReactElement) {
      return <Layout>{page}</Layout>;
    };
    
    export default Page;


    Also note, the Post type is imported from the generated types by Payload.



    Adding these notes in case someone else comes along with this issue.



    I need to add another await I think. And error checking.



    Yup, dead end. I may delete this post as it's not leading anywhere useful. At this point I will pay someone to show me what I am doing wrong.

  • default discord avatar
    baotaoh
    2 months ago

    My explanation was not that Clear. But here is the docs



    https://react.dev/learn/passing-props-to-a-component


    Give that a read it should clarify your issue.



    const Page: NextPageWithLayout = ({posts}:{posts: Post[]}) => {
      return (
        <>
          <Head>
            <title>Blog</title>
          <section>
            <div className='wrap'>
              <h1>Blog</h1>
              {posts?.length > 0 &&
                posts.map((post) => (
                  <BlogPostCard
                    key={post.id}
                    slug={post.slug}
                    title={post.title}
                    excerpt={post.excerpt}
                    displayDate={post.displayDate}
                  />
                ))}
            </div>
          </section>
        </>
      );
    };
    
    export const getStaticProps: GetStaticProps<any> = async () => {
      const postQuery = await fetch(
        `${process.env.NEXT_PUBLIC_CMS_URL}/api/posts?where[_status][equals]=published&depth=0&limit=300`,
      ).then((res) => res.json());
    
      return {
        props: {
          posts: postQuery.docs,
        },
      };
    };
    
    Page.getLayout = function getLayout(page: ReactElement) {
      return <Layout>{page}</Layout>;
    };
    
    export default Page;
  • default discord avatar
    nball
    last month

    No, your explanation was perfect, no worries there! Thank you for helping. You were right that I am not handling the optional props correctly/well.

  • default discord avatar
    lucaceck95
    last month

    I'm implementing something similar to this.


    i've got a problem... my "process.env.URL" is always undefined... what i'm doing wrong?

  • default discord avatar
    nball
    last month

    Undefined on the front end? Localhost or production?

  • default discord avatar
    lucaceck95
    last month

    localhost



    seems i forgot

    require("dotenv").config({ path: path.resolve(__dirname, "../.env") });

    on server.ts

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.