GraghQL query returning integer instead of object - reactjs

I can't figure out why none of my queries are working on my Gatsby projects.
import React from "react"
import { graphql } from "gatsby"
const site_info = () => {
const query = graphql`
query title {
allSite {
edges {
node {
siteMetadata {
title
}
}
}
}
}
`
console.log(query)
return <p>Test</p>
}
export default site_info
In the console, I'm expecting an object where I can see the title in metadata, however, I'm getting 2641826822.
I copied the query directly from GraphiQL where I'm seeing the expected result, so not sure why it isn't working here.

When you query (using a page query as you've provided) some data using GraphQL in a Gatsby schema, your data is stored as a page prop (not in the query variable itself), so you need to access to them props and iterate through the object until you find your data. An ideal structure data should look something like:
const yourPage = ({ data }) => {
const { title } = data.allSite.edges[0].node.siteMetadata;
return <Layout>
<h1>{title}</h1>
</Layout>;
};
export const yourPageData = graphql`
query title {
allSite {
edges {
node {
siteMetadata {
title
}
}
}
}
}
`;
export default yourPage;
Basically, in the snippet above I'm destructuring data as a prop (instead of doing prop.data) and I do the same with data.allSite.edges[0].node.siteMetadata to get the title.
I would recommend some documentation reading about Querying Data in Pages with GraphQL before you dive into GraphQL on the rocks.

Related

Gatsby-GraphQL - Fetching remote data from a postgres server [duplicate]

There have been a couple of similar questions, but none helped me really understand using a GraphQL inside a (class) component other than the ones in the pages folder.
My project structure looks like that:
-src
--components
---aboutBody
----index.js
--pages
---about.js
I have a page component called about (Prismic single page type) and set up some components to "fill" this page (cleaned up for better readability).
class AboutPage extends Component {
render() {
return (
<LayoutDefault>
<AboutBody
introHeadline={this.props.data.prismicAbout.data.intro_headline.text}
introParagraph={this.props.data.prismicAbout.data.intro_paragraph.text}
/>
</LayoutDefault>
)
}
}
export default AboutPage
This is what my query looks like (had it like this in both files):
export const aboutQuery = graphql`
query About {
prismicAbout {
data {
# Intro Block
intro_headline {
text
}
intro_paragraph {
text
}
}
}
}
`
(In case I am missing a bracket at the bottom, it's due to cleaning up the query example for SO — as mentioned earlier, it's working in my page component).
My graphql query is at the bottom of the AboutPage page component. It works like a charm and as intended.
But to clean this page up a bit I wanted to create appropriate components and put my query inside each component (e.g. aboutBody, aboutCarousel), again cleaned up a bit:
class AboutBody extends Component {
render() {
return (
<StyledIntro>
<h3>About</h3>
<h1>{this.props.data.prismicAbout.data.intro_headline.text}</h1>
</StyledIntro>
)
}
}
export default AboutBody
And I deleted the query from my about page component and put it inside my AboutBody component (exactly the way as shown above).
But with this it always returns the error Cannot read property 'prismicAbout' of undefined (I can't even console log the data, it always returns the same error).
I used import { graphql } from "gatsby" in both files.
Long story short, how can I achieve putting a query inside my class component and render only the component without clarifying the props in my page component like this:
class AboutPage extends Component {
render() {
return (
<LayoutDefault>
<AboutBody />
</LayoutDefault>
)
}
}
Some blogs posts mention GraphQL Query Fragments, but not sure if this is the correct use case or if it's simply a stupid beginner mistake...
That's because you can't use graphql like this in your component.
To use graphql in a component, you've got two options : useStaticQuery function or StaticQuery component, both from graphql
for useStaticQuery :
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
const MyElement = () => {
const data = useStaticQuery(graphql`
query About {
prismicAbout {
data {
intro_headline {
text
}
intro_paragraph {
text
}
}
}
}
`)
return (
<StyledIntro>
<h3>About</h3>
<h1>{this.props.data.prismicAbout.data.intro_headline.text}</h1>
</StyledIntro>
)
}
export default MyElement
with staticQuery
import React from 'react'
import { StaticQuery, graphql } from 'gatsby';
const MyElement = () => {
return(
<StaticQuery
query About {
prismicAbout {
data {
intro_headline {
text
}
intro_paragraph {
text
}
}
}
}
`}
render={data => (
<StyledIntro>
<h3>About</h3>
<h1>{this.props.data.prismicAbout.data.intro_headline.text}</h1>
</StyledIntro>
)}
/>
)
}
export default MyElement
Hope that helps you!
You can only use a query like that in a page component. One option would be to just query it in the page and then pass the data in to your component as a prop. Another is to use a static query in the component.
If your query has variables in it then you can't use a static query. In that case you should either query it all in the page and then pass it in, or you can put the part of the query related to that component in a fragment within that component's file and then use that fragment in the page query.
Example of using fragments in a component and then passing the data into the component:
// MyComponent.js
import React from "react"
import { graphql } from 'gatsby'
const MyComponent = (props) => {
const { myProp: { someData } } = props
return (
<div>
my awesome component
</div>
)
}
export default MyComponent
export const query = graphql`
fragment MyAwesomeFragment on Site {
someData {
item
}
}
`
// MyPage.js
import React from "react"
import { graphql } from "gatsby"
import MyComponent from "../components/MyComponent"
export default ({ data }) => {
return (
<div>
{/*
You can pass all the data from the fragment
back to the component that defined it
*/}
<MyComponent myProp={data.site.someData} />
</div>
)
}
export const query = graphql`
query {
site {
...MyAwesomeFragment
}
}
`
Read more about using fragments in Gatsby docs.
If you need to render the query in a class based component. This worked for me:
import React, { Component } from 'react';
import { StaticQuery, graphql } from 'gatsby';
class Layout extends Component {
render() {
return (
<StaticQuery
query={graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`}
render={data => {
return (
<main>
{!data && <p>Loading...</p>}
{data && data.site.siteMetadata.title}
</main>
)
}}
/>
);
}
}

GraphQL query displays only raw results

I have a GraphQL query kind of working (React, Gatsby, Typescript). But I only get browser to display raw json data (the "stringify" thing is what's correctly visible, raw json).
Whenever I try to use data bindings, I get error message "TypeError: undefined is not an object (evaluating 'ProjectTitles.edges.node')" in the browser. But not in console. What could be wrong? Here's the code:
import { graphql, useStaticQuery } from "gatsby";
import React, { Fragment } from "react";
const ProjectList = () => {
const ProjectTitles = useStaticQuery(graphql`
{
allMarkdownRemark(sort: { fields: frontmatter___title }) {
edges {
node {
frontmatter {
title
}
}
}
}
}
`);
return (
<Fragment>
<h2>Project titles</h2>
<div>{ProjectTitles.edges.node.frontmatter.title}</div>
{/* <div>{ProjectTitles.allMarkdownRemark.edges.node.frontmatter.title}</div> */}
{/* <div>{ProjectTitles}</div> */}
<pre>{JSON.stringify(ProjectTitles, null, 2)}</pre>
</Fragment>
);
};
export default ProjectList;
Since edges will most likely be an array you need to access a certain item or map the whole array, e.g.
{ProjectTitles.edges.map(node => (
<div>{node.frontmatter.title}</div>
))}

Can't load in images from Contentful using GatsbyJS

First time posting here, but I've been struggling with this for a few days now.
Basically I have a graphQL query that pulls in product data from contentful and then display it on a GatsbyJS page. The query correctly displays for the title, price, and description of the product but it wont load in the image. Unfortunately I keep receiving errors such as:
"TypeError: Cannot read property '0' of undefined"
or
Cannot read property 'src' of undefined. (When changing the query to look at the src of an image. When I do this method, the url does have a value according to GraphiQL)
here's the code for the page I am currently working on:
import React from 'react'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'
import Layout from '../components/Layout'
import './shop.scss'
const Shop = ({ data }) => {
return (
<Layout>
<section>
{data.allContentfulProducts.edges.map( ({ node }) => (
<article key={node.id}>
<Img
fluid={node.images.fluid}
/>
<p>{node.productName}</p>
</article>
))}
</section>
</Layout>
)
}
export const productQuery = graphql`
query {
allContentfulProducts {
edges {
node {
id
productName
images {
fluid {
...GatsbyContentfulFluid
}
}
}
}
}
}
`
export default Shop
I think there is a problem with you graphQL Query. Try this one:
export const productQuery = graphql`
query {
allContentfulProducts {
edges {
node {
id
productName
image {
fluid {
src
...GatsbyContentfulFluid
}
}
}
}
}
}
`
If this query is not helping please show us the structure of your contentful assets.

How to pass an argument to a components query (non-static)?

I am trying to create a component that I later can reuse on my website like so <TimeToRead id={someId}/>. My idea was to pass that id further down into the query.
However that does not work and ends up in: TypeError: Cannot read property 'edges' of undefined. Why is that so and what am I doing wrong here?
Is createPage(... context: id: someId) inside gatsby-node.js the only way to pass arguments? But that would only apply to templates...
How can I pass arguments to components?
import React from "react"
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome"
import { faClock } from "#fortawesome/free-solid-svg-icons"
import { graphql } from "gatsby"
const TimeToRead = ({id}) => {
console.log(id)
return (
<React.Fragment>
<FontAwesomeIcon icon={faClock} /> {timeToReadQuery.allMarkdownRemark.edges.node.timeToRead} ~ min.
</React.Fragment>
)
}
export const timeToReadQuery = graphql`
query timeToReadQuery($id: String!) {
allMarkdownRemark(
filter: { id: { eq: $id } }
) {
edges {
node {
timeToRead
}
}
}
}
`
export default TimeToRead
In Gatsby there are two types of queries. Page queries that can be defined in page components only and accept arguments passed as context in createdPage() and static queries which don't access variables and can be used in everywhere but are limited to one per file.
If you TimeToRead component file is not a page component then you have 2 options:
Use a static query - you just can't have variables in it.
Define a graphql fragment to use into parent page component.
// in child component
export const remarkTimeToReadFragment = graphql`
fragment RemarkTimeToRead on Query {
postTimeToRead: markdownRemark(id: { eq: $id }) {
timeToRead
}
}`
// in page component
export const pageQuery = graphql`
query PageQuery($id: String!) {
...RemarkTimeToRead
}
`
This particular example may produce a warning because $id param is not used directly in the page query and the linter just won't account for it being used by the fragment.

How is module/component execution order determined?

New to React/Gatsby.
As per the docs, in code below, GraphQL results are injected as {data} into IndexPage and it has to wait for GraphQL Query to complete. But how/where is this order defined?
const IndexPage = ({ data }) => (
<Layout>
//HTML
</Layout>
);
export default IndexPage;
export const allPostsQuery = graphql`
query {
some_name {
allPosts: postsConnection(orderBy: dateAndTime_DESC) {
edges {
node {
//Query...
}
}
}
}
}
`;
The execution order determined by Gatsby which wraps your React application:
You should refer to Gatsby Lifecycle APIs for the full information.
In your case, the execution of the query will come before the rendering of IndexPage.

Resources