Why does graphql not inject my data into react component prop?

Viewed 487

I am using this tutorial to create a gatsby blog that will use Markdown pages.

I am trying to create a 'post-repeater' component that will loop through all my posts and create links to them using frontmatter that looks like this.

---
path: "/blog/my-first-post"
date: "2019-05-04"
title: "My first blog post"
---

I have a graphQl query that is correctly pulling the data.

graphQl query

My component looks like this.

import React from "react"

import "../styles/post-repeater.scss"
import { graphql, Link } from "gatsby"

export default function PostRepeater({postQuery}){
    console.log('my data:',postQuery);
    return(

        <>
            {postQuery.map(instance => 
                <div className="post">
                    <h2>{instance.title}</h2>
                    <span className="datePosted">{instance.date}</span>
                    <span className="readTime"></span>
                    <p></p>
                    <Link to={instance.path}>Read More</Link>
                    <hr />
                </div>
            )}
        </>

    )
}

export const postQuery = graphql`
query MyQuery {
    allMarkdownRemark {
      edges {
        node {
          frontmatter {
            path
            title
            date
          }
        }
      }
    }
  }
`

If I put a console.log of postQuery from inside the PostRepeater component, it comes back as undefined. So it appears that the component is never getting the data even though I followed the same layout from the tutorial above.

If I put a console.log('data', postQuery); from outside the component, I get the following in the console.

data 2575425095

What am I doing wrong?

2 Answers

After making a query gatsby injects it as an object matching the query.

In your case, as you see in GraphiQL you get an object with the initial key data.

{
  "data": {
    "allMarkdownRemark": {
      "edges": [
        {
          "node": {
            "frontmatter": {
              "title": "...",
              "date": ...
            }
          }
        }
      ]
    }
  }
}

What you tried to do, is destructing a not existing key postQuery.

Moreover, you still can log it outside of the component scope as the value exported from export const postQuery

export const postQuery = graphql`
  query MyQuery {
    allMarkdownRemark {
      edges {
        node {
          frontmatter {
            path
            title
            date
          }
        }
      }
    }
  }
`;

//       v  Logs the query id
console.log('my data:', postQuery);

//                                               v Is not defined
export default function PostRepeater({ data, postQuery }) {

  //                       v Shadowing the outter value of postQuery
  console.log('my data:', postQuery);

  // The query injected as "data" object
  console.log('my data:', data);

  return (
    <>
      //      v Be aware of which object you query, edges is an array
      {data.allMarkdownRemark.edges.map(...)}
    </>
  );
}

The export of graphql queries which gets automatically attached to props only work for page components. For any non-page components you need to use either the StaticQuery component or the useStaticQuery hook.

Read more here: https://www.gatsbyjs.com/docs/static-query/

Here is how your code should look like using the useStaticQuery hook

import React from "react"

import "../styles/post-repeater.scss"
import { graphql, useStaticQuery, Link } from "gatsby"

export default function PostRepeater() {
  const data = useStaticQuery(graphql`
    query MyQuery {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              path
              title
              date
            }
          }
        }
      }
    }
    `)
   // you might have multiple nodes in which case you must use filter 
   // or find to get node which contains your data. 
   const postQuery = data.allMarkdownRemark.edges[0].node.frontmatter;

   return (
     <>
       {postQuery.map(instance => 
         <div className="post">
           <h2>{instance.title}</h2>
           <span className="datePosted">{instance.date}</span>
           <span className="readTime"></span>
           <p></p>
           <Link to={instance.path}>Read More</Link>
           <hr />
         </div>
       )}
     </>
   )

}
Related