GraphQL query displays only raw results - reactjs

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>
))}

Related

Type '{}' is missing the following properties from type 'RouteComponentProps<{},,>'

So I'm quite new to React, and especially new to Typescript. I'm trying my best to wrap my head around it all so bare with me!
index.tsx
Router.tsx (all of the different routes)
LandingFrame.tsx (page layout)
import React from 'react';
import LandingMain from './LandingMain'
const LandingFrame = () => {
return (
<LandingMain/>
);
}
export default LandingFrame;
LandingMain.tsx (main)
import React, { forwardRef } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { useQuery } from '#apollo/react-hooks';
import gql from 'graphql-tag';
const YES = gql`
query yes {
yes {
id
data
data {
data
data
}
}
}
`;
const LandingMain = ({ history }: RouteComponentProps<{}>) => {
const { loading, error, data } = useQuery(YES);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>landing-page.js</h1>
{data.yes
? <p>Welcome back, {data.yes.data}</p>
: <p>Welcome back, Anon</p>
}
</div>
);
};
export default LandingMain;
(on a 2nd note) I'm hoping that by splitting the landing page, the frame would load first, following the graphql query in the main component. That way when the page loads it doesn't only return <p>Loading...</p> on a blank page before loading the rest.
TypeScript error in LandingFrame.tsx(22,14):
Type '{}' is missing the following properties from type 'RouteComponentProps<{}, StaticContext, PoorMansUnknown>': history, location, match TS2739
At this point I'm just trying to split up as much code as I can. But while doing so I noticed that TS won't let me import a component as easily as React would. From what I've read, I need to pass props somehow?
return (
<LandingMain/>
);
LandingMain expects to recieve routeComponentProps, but you are rendering it without said props. You need to add the props expected by the component.
It's my understanding that landing frame is also receiving RouteComponentProps, so this should work
const LandingFrame = (props: RouteComponentProps<{}>) => {
return (
<LandingMain {...props} />
);
}

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>
)
}}
/>
);
}
}

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.

Map() function returning as undefined when attempting to return query

I'm having issues returning data from a simple query. I properly executed this with Apollo's test application (https://codesandbox.io/s/nn9y2wzyw4), but when I attempt it with data from my local server it is throwing me a TypeError: Cannot read property 'map' of undefined. The query is showing up in my console log and on the GraphQL playground.
The only way to return data I have found so far is the map function, but I realize it may not be applicable in this situation. I have tried formatting my return statement a million ways but do not seem to get it. I understand it is likely a very simple solution.
import React from "react";
import { render } from "react-dom";
import ApolloClient from "apollo-boost";
import { ApolloProvider, useQuery } from "#apollo/react-hooks";
import gql from "graphql-tag";
const client = new ApolloClient({
uri: "http://localhost:4000/graphql"
});
function GetPokemon() {
const { loading, error, data } = useQuery(gql`
{
pokemonById(id: "003") {
id
name
}
}
`);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
console.log(data);
return data.pokemonById.map((id, name) => (
<div key={id}>
<p>
{id}: {name}
</p>
</div>
));
}
export const App = () => (
<ApolloProvider client={client}>
<div>
<h2>My first Apollo app 🚀</h2>
<GetPokemon />
</div>
</ApolloProvider>
);
For more context, I am using a local version of this GraphQL server: https://github.com/axelhzf/graphql-pokemon-server
And in the playground, my query appears as the following:
{
"data": {
"pokemonById": {
"id": "003",
"name": "Venusaur"
}
}
}
You are trying to use map on an object.
return (
<div key={id}>
<p>
{data.pokemonById.id}: {data.pokemonById.name}
</p>
</div>
);

GraphQL query works in Gatsby page but not inside class component

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>
)
}}
/>
);
}
}

Resources