Gatsby newbie in here, so be patient with me. I’m building a very basic blog app with Gatsby and React. I have a landing page with a section where I display the latest 3 blog posts that were published. Until now, everything worked fine with the following code in my index.js
file:
import React from 'react'
import { Link, graphql } from 'gatsby'
import get from 'lodash/get'
import Bio from '../components/Bio'
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import { formatReadingTime } from '../utils/helpers'
class BlogIndex extends React.Component {
render() {
const siteTitle = get(this, 'props.data.site.siteMetadata.title')
const siteDescription = get(
this,
'props.data.site.siteMetadata.description'
)
const size = 3
const posts = get(this, 'props.data.allMarkdownRemark.edges')
return (
<div className="container">
<Layout location={this.props.location} title={siteTitle}>
<SEO />
<Bio />
<section>
<div>
{posts.slice(0, size).map(({ node }) => {
const title = get(node, 'frontmatter.title') || node.fields.slug
return (
<div key={node.fields.slug}>
<h3>
<Link to={node.fields.slug}>
{title}
</Link>
</h3>
<p>
{node.frontmatter.date}
{` • ${formatReadingTime(node.timeToRead)}`}
</p>
<p
dangerouslySetInnerHTML={{ __html: node.frontmatter.spoiler }}
/>
</div>
)
})}
</div>
</section>
</Layout>
</div>
)
}
}
export default BlogIndex
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
description
}
}
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
edges {
node {
fields {
slug
}
timeToRead
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
spoiler
}
}
}
}
}
`
However, I would like to separate the latest blog posts section into a separate component (ex. LatestBlogPosts.js
) and import it in my index.js
file. Therefore, I copied the markup in a separate file, imported that file in my index.js
and all of a sudden there is an error saying:
TypeError: Cannot read property 'slice' of undefined
I checked and logging posts
from the newly created file also returns undefined
. The LatestBlogPosts.js
file looks like this:
import React from 'react'
import { Link } from 'gatsby'
import {formatReadingTime} from "../utils/helpers";
import get from 'lodash/get'
class LatestBlogPosts extends React.Component {
render() {
const size = 3
const posts = get(this, 'props.data.allMarkdownRemark.edges')
return (
<section>
<div>
{posts.slice(0, size).map(({ node }) => {
const title = get(node, 'frontmatter.title') || node.fields.slug
return (
<div key={node.fields.slug}>
<h3>
<Link to={node.fields.slug}>
{title}
</Link>
</h3>
<p>
{node.frontmatter.date}
{` • ${formatReadingTime(node.timeToRead)}`}
</p>
<p
dangerouslySetInnerHTML={{ __html: node.frontmatter.spoiler }}
/>
</div>
)
})}
</div>
</section>
)
}
}
export default LatestBlogPosts
And the modified index.js
looks like this:
import React from 'react'
import { Link, graphql } from 'gatsby'
import get from 'lodash/get'
import Bio from '../components/Bio'
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import Footer from '../components/Footer'
import { formatReadingTime } from '../utils/helpers'
import { rhythm } from '../utils/typography'
import LatestBlogPosts from "../components/LatestBlogPosts";
class BlogIndex extends React.Component {
render() {
const siteTitle = get(this, 'props.data.site.siteMetadata.title')
const siteDescription = get(
this,
'props.data.site.siteMetadata.description'
)
return (
<div className="container-fluid">
<Layout location={this.props.location} title={siteTitle}>
<SEO />
<Bio />
<LatestBlogPosts />
<Footer />
</Layout>
</div>
)
}
}
export default BlogIndex
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
description
}
}
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
edges {
node {
fields {
slug
}
timeToRead
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
spoiler
}
}
}
}
}
`
I can’t seem to understand why posts
returns undefined
in the LatestBlogPosts.js
file?
2
Answers
The problem lies in this expression:
In your first example,
this
points toBlogIndex
; in your second example, you’ve moved the expression to therender()
method ofLatestBlogPosts
, so the expression will look for the data on that component.You need to pass the posts to the child component as a prop:
And reference it in the child’s render method:
You’re not passing
posts
toLatestBlogPosts
. Try:<LatestBlogPosts posts={posts} />
then in
LatestBlogPosts
you can access withthis.props.posts
.