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.
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>;
};
@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
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?
The error is in browser. Unhandled Runtime Error
TypeError: posts.map is not a function
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
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
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
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.
My explanation was not that Clear. But here is the docs
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;
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.
I'm implementing something similar to this.
i've got a problem... my "process.env.URL" is always undefined... what i'm doing wrong?
Undefined on the front end? Localhost or production?
localhost
seems i forgot
require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
on server.ts
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.