Context
At present time, Gatsby treats files in src/pages and src/layouts in a special way. If either file contains a graphQL query, during the build process, Gatsby will run that query can pass the result into the component exported using the default export of the file as a data prop.
For example the following succeeds:
// src/pages/index.js
import Container from './HomePage/Container;
export default HomePage;
export const query = graphql`
...
`
Problem
Because of the way Gatsby parses these files, the query must exist within the file - in the case that queries are stored in a different directory (for example if using the directory component pattern where there is a root 'Container' component that pulls in data). Gatsby does not associate the query with the page, doesn't run it and doesn't pass the result into the component as a data object.
With the following, the query isn't run and the result isn't passed to the component, so the data from will be undefined.
// src/pages/index.js
import Container,{query} from './HomePage/Container;
export default HomePage;
export {query};
This is surprising and currently undocumented. It is also a hard problem to debug if this issue isn't expected - everything looks fine and compiles fine, however runtime errors will occur because the data prop will be undefined.
Cause
packages/gatsby/src/internal-plugins/query-runner/file-parser.js only checks the AST for named exports see here, so if the query is in its own file and uses a default export it doesn't find, parse or run the query.
If the component in src/layouts or src/pages doesn't actually contain the graphql query (in other words if instead it imports, then exports a query, as in my example above), Gatsby doesn't find the graphql query during preprocessing so doesn't associate the query with the component or run the query. In the case of Layouts and Pages, this means the query result isn't passed to the component as a data property.
Solution
Gatsby needs to be aware of graphql queries imported into files/components so that it can associate those queries with the files/components. It also needs to check for queries defined in default exports in cases where the queries have been moved to their own dirs, for example:
– data
– queries
– aboutPage
– homePage
Additionally I think adding a section to the docs about the 'magic' Gatsby is performing in terms of preprocessing files, running queries and injecting both context into the queries and data into component props would be really helpful for understanding what is happening.
Finally it might make sense for Gatsby to notice when no data object is passed into a component in pages or layout and at least give a warning with a suggested soliution, i.e.
No query was run for page 'index'. Did you mean to export a graphql query for this file.
See related discussion in #2800.
This tripped me up as well—especially the lack of errors (if I recall correctly, in my case there were none but my components just didn't render). On my growing list of small PRs I want-to-make-but-haven't-yet.
I've been solving it so far by querying in the page, and then just passing the results into a data prop on components. Would much prefer to be able to encapsulate that logic within the components themselves.
@dustinhorton have you seen fragments? That's the approved way to split queries up amongst components.
@KyleAMathews Fragments are great. One feature I would like to see implemented is to declare fragments out of the ./src/ folder, like, in a node module GraphQLString or a .graphql file that's imported into the page or layout.
I've love to see gatsby work with importing fragments from files, but I think we'd run into some serious trouble on that front. GQL queries are all statically extracted and compiled which makes doing stuff like importing _really_ hard. At the moment the query parser only needs to understand tagged template literals, to handle a case were a page imports a query you'd need to:
It gets even more complicated because any plugin or user can adjust the module resolution logic in webpack, or that the file on the other end might be coffeescript or typescript or a markdown file with frontmatter, etc. It may be feasible to do but I think there is a reason that both Relay and Apollo _don't_ do it, and especially so that Relay had to migrate away from that model in order to get static querying to work.
As a side note you can store your info in another file and spread it into the page, but not via an import
export graphql` query { ...Fragment_fromAnotherPage }`
@jquense Thanks for the explanation. I thought it was going to be a difficult one. In the short term, might it be possible to look at:
Thinking about this a bit more, could some of the problems be negated by using files with a .graphql extension? That would greatly limit the potential possibilities.
@KyleAMathews Have seen the term mentioned here, but wasn't sure what it is. Hopefully I'll get to look into it tomorrow—thanks for the tip.
@dustinhorton yeah really need to document it but in the meantime, you can search the repo as a number of example sites use them.
@Undistraction yeah, .graphql files are likely much easier to support.
Hello,
Just to let you know that I just lost a couple of hours to that, I put a query in a component and kept getting "undefined".
It was very weird one to debug as the query was successful in GraphiQL, in my opinion this badly needs documenting.
It's also been one of these things that felt kind of "magical", like:
"OK, so you can have another export and somewhat gatsby will recognise wether it's a query or not and then execute it"
I'm not sure what's the best way to make this explicit while preserving convenience, but maybe something like :
import React from 'react';
import {graphql} from 'gatsby';// or wherever
export default ({ data }) => {
console.log(data);
return <div>File List</div>;
};
// maybe add a webpack rule for .graphql files ?
// then you could do something like "import query from 'query.graphql' ";
export graphql`
query MyFilesQuery {
allFile {
edges {
node {
relativePath
prettySize
extension
birthTime(fromNow: true)
}
}
}
}
`;
Just my two cents, obviously!
And thank you for the great work on Gatsby, I think it's game changing!
I have few pages that require very similar set of data. I've decided to make one big query that cover all needs for every page and move it to another file to import it in every page I have.
Took me several hours to figure out I'm not allowed to do this...
is there a solution yet?
the solution is to compose fragments in other files together: https://facebook.github.io/relay/docs/en/fragment-container.html#composing-fragments
I'm wondering this. I've got quite a large query in my index page and i'd like to separate this out into an imported query per component i'm passing down to
@trickydisco78 Or you could create a js file and inside it, export a const with the query if there's no parameters involved for instance.
@trickydisco78 Gatsby added StaticQuery since the lifetime of this issue: https://www.gatsbyjs.org/docs/static-query/
Most helpful comment
@KyleAMathews Fragments are great. One feature I would like to see implemented is to declare fragments out of the
./src/folder, like, in a node module GraphQLString or a.graphqlfile that's imported into the page or layout.