Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Problem using map to parse getStaticProps

default discord avatar
nballlast year
14

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
    baotaohlast year

    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
    nballlast year
    @966272641260462121

    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
    baotaohlast year

    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
    nballlast year

    The error is in browser. Unhandled Runtime Error


    TypeError: posts.map is not a function

  • default discord avatar
    baotaohlast year

    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
    nballlast year

    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
    baotaohlast year

    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
    nballlast year

    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
    baotaohlast year

    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
    nballlast year

    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
    lucaceck95last year

    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
    nballlast year

    Undefined on the front end? Localhost or production?

  • default discord avatar
    lucaceck95last year

    localhost



    seems i forgot

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

    on server.ts

  • default discord avatar
    sammymlgslast year

    I’ve got this same problem with a similar code. Says map is not a function.


    Code literally looks identical to yours.


    Have you found a solution please ???

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.