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

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

Related

Using a static query in a React class component

I trying to use a static query in a react class component in Gatsby.
import * as React from "react"
import '../styles/layout.scss'
import Layout from '../components/layout'
import { useStaticQuery, graphql } from 'gatsby'
import { container } from '../styles/mystyles.module.scss'
class IndexPage extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Layout>
<div>
<h1 className={container}>hello </h1>
<div>This is my container</div>
</div>
</Layout>
}
}
const data = useStaticQuery(graphql`
query HeaderQuery {
site {
siteMetadata {
title
}
}
}
`)
export default IndexPage
I get the error
19:14 error React Hook "useStaticQuery" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Does this mean it is not possible and it has to be a function component?
The issue is that you're trying to call a function at the “top level” (out of a function), and hooks only work when they are called from inside a function component. E.g.:
const YourComponent = () => {
const data = useStaticQuery(yourQuery)
}
If you don’t want to use a function component, you can still use the StaticQuery higher-order component instead:
export default (props) =>
<StaticQuery query={yourQuery}>
{({ data }) => <SomeComponent {...data} {...props} />}
</StaticQuery>
…or like this…
class SomeComponent extends React.Component {
render() {
return (
<StaticQuery query={yourQuery}>
{({ data }) => <div>{data.site.title}</div>}
</StaticQuery>
)
}
}

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

Issue with testing Wrapper Query Component

I'm very new to React testing and I'm trying to write Jest/Enzyme Unit tests for the MyReload wrapper component on top of Apollo Query that I have created. This basically adds refetchers to an array (that can be executed at later point of time).
Usage:
<MyReloadQuery id='1234 .....> // do something.. children </MyReloadQuery>
Component:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Subscribe } from "unstated";
import { Query } from "react-apollo";
import { MyReloadContainer } from "./MyReloadContainer";
export class MyReloadQuery extends Component {
static propTypes = {
children: PropTypes.any
};
componentWillUnmount() {
this.onUnmount();
}
render() {
return (
<Subscribe to={[MyReloadContainer]}>
{refetchers => (
<Query {...this.props}>
{(...args) => {
const { refetch } = args[0];
this.onUnmount = () => {
refetchers.removeRefetcher(refetch);
};
refetchers.addRefetcher(refetch);
return this.props.children(...args);
}}
</Query>
)}
</Subscribe>
);
}
}
I'm very new to React and it's testing and I'm wondering how can one test with dummy data for this one?
Can someone please enlighten me? Any help would be appreciated :pray:

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

react, gatsby - how to access GraphQL query data in react class component

I've a react component named 'Header' in /src/components directory in gatsby defined as below:
import React, { Component } from "react"
import { Link } from 'gatsby'
import { StaticQuery, graphql } from 'gatsby'
import Img from "gatsby-image"
import "./header.css"
class Header extends Component {
constructor(props) {
super(props);
}
render() {
return(
<Img fixed={data.file.childImageSharp.fixed} />
)
}
}
export default Header
export const query = graphql`
query {
file(relativePath: { eq: "logo.png" }) {
childImageSharp {
# Specify the image processing specifications right in the query.
# Makes it trivial to update as your page's design changes.
fixed(width: 125, height: 125) {
base64
}
}
}
}
`
I tried to use StaticQuery with function component but it didn't work. So, I want to stick to the class component syntax.
Here, the query executes fine in http://localhost:8001/___graphql
How do I access query data in react class component ?
Only page component files can export a query variable to query like your example shows. See Use a page query in the Gatsby documentation.
Here's an example of StaticQuery usage with a class component. You have to wrap the class component with a functional component to make it work.
import React from 'react'
import { graphql, StaticQuery } from 'gatsby'
class CoolThing extends React.Component {
render () {
console.log(this.props)
return (
<div>
{this.props.site.siteMetadata.title}
{/* conditionally render directories */}
{this.props.directories.edges && (
<ul>
{this.props.directories.edges.map((directory) => {
return <li key={directory.node.id}>{ directory.node.name }</li>
})}
</ul>
)}
</div>
)
}
}
export default () => (
<StaticQuery
query={graphql`
query {
site {
siteMetadata {
title
}
}
allDirectory {
edges {
node {
id
name
}
}
}
}
`}
render={(data) => (
<CoolThing site={data.site} directories={data.allDirectory} />
)}
/>
)
Here is a related discussion on the topic.
You may also consider using Gatsby's useStaticQuery hook.

Resources