How is module/component execution order determined? - reactjs

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.

Related

GraghQL query returning integer instead of object

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.

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

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.

Modifying data from graphql queries into react components using gatsbyjs works in gatsby develop but breaks in gatsby build

I'm using prismic as a CMS for a website built with gatsby.
I need to manipulate the data returned by graphql queries before rendering it in the react component.
The website works fine in dev but the build fails because the variables I'm using are not defined at build time.
I've tried using componentDidMount and the hooks equivalent to define my variables only at mount time but it didn't work. I've also tried assigning the variable to the state of the component at mount time but that failed as well. See the code below, where I tried to make a simple example, for a better idea:
import { graphql } from 'gatsby';
import Layout from "../components/layout"
export const data = graphql`
query allData {
allPrismicNews{
edges {
node {
id
}
}
}
}
`;
class IndexPage extends Component {
render() {
return (
<Layout>
<p>{this.state.newsId ? this.state.newsId : null}</p>
</Layout>
);
}
componentDidMount() {
if (typeof window === 'undefined') {
return;
}
this.setState(() => ({ newsId: this.props.data.allPrismicNews.edges.map(article=>article.node.id).flat() }));
}
}
export default IndexPage;```
For this example, I expect to see the ids of the news output in the template, this works in development but not in production.
What am I doing wrong?
What you could do is set an initial state to your newsId so that this.state.newsID is never undefined:
class IndexPage extends Component {
state = {
newsId: null,
}
componentDidMount() {
if (typeof window === "undefined") {
return
}
this.setState({
newsId: this.props.data.allPrismicNews.edges
.map(article => article.node.id)
.flat(),
})
}
render() {
return (
<Layout>
<p>{this.state.newsId ? this.state.newsId : null}</p>
</Layout>
)
}
}

How to force update data cache in react-apollo?

How to refetch fresh data when you revisit a page whose data is powered by react-apollo?
Say, I visit a listing page for the first time. apollo will fetch the query and caches it by default. So, when you visit the same page again during the session, it will populate the data from its cache store. How to force apollo to refetch data every time when the component mounts?
You can use apollo's fetchPolicy. Based on this, it will decide to execute the query or not again.
Example:
const graphQLOptions = {
name: 'g_schemas',
options: (props) => {
return {
variables: {
name: props.name,
},
fetchPolicy: 'cache-and-network',
}
},
}
Hope it helps.
Adding to Pranesh's answer: the fetchPolicy you're looking for is network-only.
In case you are using react-apollo's Query component, for example:
import { Query } from "react-apollo";
You can apply the fetchPolicy through its props. See below an example:
import gql from 'graphql-tag';
import React from 'react';
import { Query } from 'react-apollo';
const CounterView = ({ counter }) => (
<div>{counter}</div>
);
const GET_COUNTER = gql`
{
counter
}
`;
const Counter = () => (
<Query query={GET_COUNTER} fetchPolicy={'network-only'}>
{({ data }) => {
return <CounterView {...data} />;
}}
</Query>
);
export default Counter;
References:
https://www.apollographql.com/docs/react/essentials/queries.html#basic
https://www.apollographql.com/docs/react/essentials/queries.html#props
https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-config-options-fetchPolicy

Resources