skip to Main Content

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


  1. The problem lies in this expression:

    posts = get(this, 'props.data.allMarkdownRemark.edges')
    

    In your first example, this points to BlogIndex; in your second example, you’ve moved the expression to the render() method of LatestBlogPosts, so the expression will look for the data on that component.

    You need to pass the posts to the child component as a prop:

    <LatestBlogPosts posts={posts} />
    

    And reference it in the child’s render method:

    {this.props.posts.slice(...
    
    Login or Signup to reply.
  2. You’re not passing posts to LatestBlogPosts. Try:

    <LatestBlogPosts posts={posts} />

    then in LatestBlogPosts you can access with this.props.posts.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search