Gatsby: Separating GraphQL queries into separate files

Created on 11 Jun 2019  路  11Comments  路  Source: gatsbyjs/gatsby

Summary

I'm trying to find a way to increase the modularity of my GrahphQL queries. On some components there are 4 queries needed, which produces these fat and nasty looking components. I've tried creating other files and importing queries as strings, but that sometimes work and some other times doesn't.

Relevant information

Think of a box container where you have 4 boxes, each having their own image. I'm using gatsby-background-image, gatsby-image, gatsby-plugin-sharp, and gatsby-transformer-sharp.

Bear in mind that this is not an error or a bug, i'm just seeking out a way to increase modularity of my application.

question or discussion

Most helpful comment

imageQuery is a react hook/function so you need to call it to fetch the data rather than render it directly.

See the example here:
https://www.gatsbyjs.org/docs/use-static-query/#composing-custom-usestaticquery-hooks

E.g.
I've renamed it to useImageQuery as since it is a hook you should follow naming conventions.

import React from "react"
import useImageQuery from "../queries/imageQuery"

import Box from "../components/boxes/box"

const Test = () => {
  const { newVehicleDesk } = useImageQuery()
  return (
    <div>
      <p>we are testing here mate</p>
      <Box imageData={newVehicleDesk} />
    </div>
  )
}

export default Test

All 11 comments

Have you considered breaking the queries up into fragments that could live in different files?

https://www.gatsbyjs.org/docs/using-fragments/

I agree, fragments are the way to go here. I often keep a /fragments directory with a separate file for each fragment.

The nice thing is, when compiling your site, Gatsby preprocesses all GraphQL queries it finds. This means any file that gets included in your project can define a fragment by exporting a graphql-tagged string. These are made available globally. So unlike the actual string representation of a query, you don't need to import them manually in your components.

Well, I think fragments do not address my issue. Imagine the case where we need 4 images for a component like this, and then we'd like to use the very 4 images in another component.

How would you DRY this? currently i'm repeating myself.

I would pull the query out into a separate file under a /hooks folder and then reference that from the components that require the four images. I'd also create a FluidImage fragment to reduce duplication (this could be put in its own /fragments/fluidImage.js file if used in more than one query).

E.g.

// useImageData.js
import { graphql, useStaticQuery } from "gatsby"

const useImageData = () => {
  const imageData = useStaticQuery(
    graphql`
      fragment FluidImage on File {
        childImageSharp {
          fluid(quality: 70, maxWidth: 1160) {
            ...GatsbyImageSharpFluid
          }
        }
      }
      query {
        newVehiclesDesk: file(relativePath: { eq: "wild-kiwi-new-vehicles.jpg" }) {
          ...FluidImage
        }
        localGuidsDesk: file(relativePath: { eq: "wild-kiwi-local-guides.jpg" }) {
          ...FluidImage
        }
        smallGroupsDesk: file(relativePath: { eq: "wild-kiwi-small-groups.jpg" }) {
          ...FluidImage
        }
        breathTakingSceneryDesk: file(relativePath: { eq: "wild-kiwi-breathtaking-scenary.jpg" }) {
          ...FluidImage
        }
      }`)
  return imageData
}

export default useImageData

I've created a framents.js and imageQuery.js to store my queries and fragments;

imageQuery looks like this:

import { graphql, useStaticQuery } from "gatsby"

const ImageQuery = () => {
  const ImageData = useStaticQuery(graphql`
    query {
      newVehicleDesk: file(relativePath: { eq: "wild-kiwi-new-vehicles.jpg" }) {
        ...FluidImage
      }
    }
  `)
  return ImageData
}

export default ImageQuery

and the fragments.js looks like this:

import { graphql } from "gatsby"

export const FluidImageFragment = graphql`
  fragment FluidImage on File {
    childImageSharp {
      fluid(quality: 70, maxWidth: 1160) {
        ...GatsbyImageSharpFluid
      }
    }
  }
`

The component in which I'm using the Query?

import React from "react"
import ImageQuery from "../queries/imageQuery"

import Box from "../components/boxes/box"

const Test = () => {
  return (
    <div>
      <p>we are testing here mate</p>
      <Box imageData={ImageQuery} />
    </div>
  )
}

export default Test

For some unknown reasons the query is not being exerted, i've consoled it and this is what I get:

茠 ImageQuery() {
  var ImageData = Object(gatsby__WEBPACK_IMPORTED_MODULE_0__["useStaticQuery"])("170197051");
  return ImageData;
}

The problem is that if I use ImageQuery, we are just returning a function string, when I use ImageQuery.ImageData I'll get undefined....

What am I missing here?

imageQuery is a react hook/function so you need to call it to fetch the data rather than render it directly.

See the example here:
https://www.gatsbyjs.org/docs/use-static-query/#composing-custom-usestaticquery-hooks

E.g.
I've renamed it to useImageQuery as since it is a hook you should follow naming conventions.

import React from "react"
import useImageQuery from "../queries/imageQuery"

import Box from "../components/boxes/box"

const Test = () => {
  const { newVehicleDesk } = useImageQuery()
  return (
    <div>
      <p>we are testing here mate</p>
      <Box imageData={newVehicleDesk} />
    </div>
  )
}

export default Test

Thanks a lot mate! I think I've gotta browse through the documentation more meticulously... I've overlooked the fact that we are doing custom hooks here.

Here is my component now;

const Test = () => {
  const { newVehicleDesk } = useImageQuery()
  console.log(newVehicleDesk)
  return (
    <div>
      <p>we are testing here mate</p>
      <Box
        imageData={newVehicleDesk.childImageSharp.fluid}
      />
    </div>
  )
}

I don't really love the idea of having that .childImageSharp.fluid after each query, I've tried adding it in the imageQuery.js like this:

const useImageQuery = () => {
  const ImageData = useStaticQuery(graphql`
    query {
      newVehicleDesk: file(relativePath: { eq: "wild-kiwi-new-vehicles.jpg" }) {
        ...FluidImage
      }
    }
  `)
  return ImageData.newVehicleDesk.childImageSharp.fluid
}

But it becomes undefined in the component. Have you got a way around this?

Your last example should work, make sure you change the Test component to not use the destructuring assignment when calling useImageQuery.

E.g.

const Test = () => {
  const fluidImage = useImageQuery()
  return (
    <div>
      <p>we are testing here mate</p>
      <Box imageData={fluidImage} />
    </div>
  )
}

Yea, I realised that quickly. Your help is much appreciated.

Cheers mate.

@Polyhistor @jrestall Thanks a lot, guys!

@Polyhistor @jrestall Thanks guys, that's really helped me :heart:

Was this page helpful?
0 / 5 - 0 ratings