Im developing a React Native app using Relay modern, GraphQL, and Graphcool. I'm trying to fetch the posts from the DB, and I have 3 files, Post.js, PostList, and the index.js.
PostList.js:
export default createFragmentContainer(PostList, graphql`
fragment PostList_viewer on Viewer {
allPosts(last: 100, orderBy: createdAt_ASC) #connection(key: "PostList_allPosts", filters: []) {
edges {
node {
...Post_post
}
}
}
}
`)
Post.js
export default createFragmentContainer(Post, graphql`
fragment Post_post on Post {
id
content
createdAt
author {
id
username
}
}
`)
index.js
const Feed = () => (
<QueryRenderer
environment={environment }
query={AllPostQuery}
render={({ error, props }) => {
if (error) {
return <div>{error.message}</div>
} else if (props) {
return <PostList viewer={props.viewer} />
}
return <Text>Loading</Text>
}}
/>
)
When I console log this.props.viewer.allPosts inside PostList.js I get { edges: [ null, null, null ], .... I have 3 posts in the DB so it's finding the posts, but why are they null? Pl
first of all if you run into problems try to log the "root" object which is props not a branch inside of it - this will make easier for you to undestrand data structure.
Also, as far as I know Relay does some magic which makes data available for you only in files where you defined a fragment, so in PostList.js you will not see details of the post. Try printing data from Post.js. That's possibly the reason why you see null values printed.
Also data from GraphQL be will be available for you under variable name derived from fragment name after _ (underscore), so in your example it will be post not allPosts.
Related
Gatsby noob here so please bear with me. I have a component that accepts props from the index.js where it is supposed to receive data from an array of objects but will always receive the error TypeError: Cannot read property 'map' of undefined where it's referring to the Hero.js component index.js is calling for.
My assumption is that the data being queried in index.js is either not specific enough or that it is rendering the component before data is received. Here is the index.js file:
import { graphql } from 'gatsby';
import { Layout, SEO, Hero } from 'components';
const IndexPage = ({ data }) => {
const dataFetch = data.contentfulTemplateIndex.heroes;
let tester = () => {
for (let count = 0; count < dataFetch.length; count++) {
return <Hero {...props} />;
}
};
console.log(dataFetch);
let props = {
impactText: dataFetch.impactText,
labels: dataFetch.labels,
primaryLabel: dataFetch.primaryLabel,
location: dataFetch.location
// supportingText: dataFetch.supportingText.json
};
return (
<Layout>
{dataFetch && tester()}
</Layout>
);
};
export const query = graphql`
query {
contentfulTemplateIndex {
heroes {
image {
fluid {
src
}
}
impactText
labels
location
primaryLabel
supportingText {
json
}
}
}
}
`;
export default IndexPage;
Here is the Hero.js component which index.js is calling:
import { Link } from 'gatsby';
import { documentToReactComponents } from '#contentful/rich-text-react-renderer';
import cx from 'classnames';
import styles from './hero.module.scss';
const Hero = (props) => {
return (
<div>
<ul>
<Link className={styles.pills}>{props.primaryLabel}</Link>
{props.labels.map((label) => {
return <Link className={styles.pills}>{label}</Link>;
})}
</ul>
<div className={styles.grid}>
<h1>{props.impactText}</h1>
</div>
</div>
);
};
export default Hero;
It's impossible for an outsider to debug your code without a minimum reproducable example.
The best way to debug GraphQL is to use the GraphiQL interface of your browser.
Run gatsby develop. If it fails because of the TypeError remove the lines of code that cause the type error (but not the code of your GraphQL query!). You need to get your development server runnning.
Open your browser, use the URL: http://localhost:8000/___graphql
Copy your graphQL query from your code and paste it into the GraphiQL query window.
Can you access your data there? If not you made a mistake writing your query or the data is not where it's supposed to be.
This way you can make sure the data exists.
It also helps to console.log(props) so you can examine the data object:
const Hero = (props) => {
console.log(props);
return (
I am working on a blog using Gatsby and I am trying to generate pages based on that tag a user clicks on.
When a user clicks on a tag, he should be redirected to a page that displays a list of articles with that tag.
I created a query that does that. However, my GraphQL query works in my GraphQL editor but not in my react code. It returns an empty array in the code but the expected result when I run the query in the Graphql editor.
import React from "react"
import ArticleCard from "../articleCard/ArticleCard"
import Pagination from "../pagination/Pagination"
import articlesStyles from "./Articles.module.scss"
import { useStaticQuery, graphql } from "gatsby"
const Articles = ({ pageTitle }) => {
const blogPosts = useStaticQuery(graphql`
query($tag: String) {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___publish_date] },
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
nodes {
frontmatter {
title
publish_date
imageUrl
author
category
}
fields {
slug
}
excerpt(pruneLength: 100)
}
}
}
`)
const nodes = blogPosts.allMarkdownRemark.nodes;
console.log(nodes)
return (
<div className={articlesStyles.contentArea}>
<h1 className={articlesStyles.headerTitle}>{pageTitle}</h1>
<div className={articlesStyles.articles}>
{nodes.map(node => {
return (
<ArticleCard
key={node.id}
{...node.frontmatter}
excerpt={node.excerpt}
slug={node.fields.slug}
/>
)
})}
</div>
<Pagination />
</div>
)
}
export default Articles
As mentioned in the comments, you can't use variables in a staticQuery.
However, this may change in the future as many people stumble upon this limitation and there is work in progress to provide 'dynamicQueries'.
In the meanwhile you can use variables in queries in gatsby-node.js and the templates used by the createPage API. So, in your case, you could generate all the pages for all the tags in gatsby-node.js and still use the same article.js as component template.
See: https://www.gatsbyjs.org/docs/adding-tags-and-categories-to-blog-posts/
As #xadm and #ksav noted in their comments:
"Known Limitations useStaticQuery does not accept variables"
Here as a workaround
Query for all blogs, then use Array.prototype.filter() to get the one you need:
// ...
const blogPosts = useStaticQuery(graphql`
query {
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___publish_date] }
// you can't filter here
) {
nodes {
frontmatter {
title
publish_date
imageUrl
author
category
}
fields {
slug
}
excerpt(pruneLength: 100)
}
}
}
`)
// Get all blogs
const { blogPosts: { allMarkdownRemark: { edges } } } = props;
// filter the blogs
const yourFilteredTag = edges.filter(
el => el.node.childMarkdownRemark.frontmatter.tag === yourTagVariable);
// Now you can get the filtered list of blogs
console.log(yourFilteredTag);
// ...
Edit
As #Albert Skibinski mentioned this solution is not scalable. The context in gatsby-node.js might be a better solution in your use case.
The Array.prototype.filter() solution I described should be reserved for components where the context variable is not available and you can make sure that the size of the array does not grow excedeedingly large.
I am using this tutorial to create a gatsby blog that will use Markdown pages.
I am trying to create a 'post-repeater' component that will loop through all my posts and create links to them using frontmatter that looks like this.
---
path: "/blog/my-first-post"
date: "2019-05-04"
title: "My first blog post"
---
I have a graphQl query that is correctly pulling the data.
My component looks like this.
import React from "react"
import "../styles/post-repeater.scss"
import { graphql, Link } from "gatsby"
export default function PostRepeater({postQuery}){
console.log('my data:',postQuery);
return(
<>
{postQuery.map(instance =>
<div className="post">
<h2>{instance.title}</h2>
<span className="datePosted">{instance.date}</span>
<span className="readTime"></span>
<p></p>
<Link to={instance.path}>Read More</Link>
<hr />
</div>
)}
</>
)
}
export const postQuery = graphql`
query MyQuery {
allMarkdownRemark {
edges {
node {
frontmatter {
path
title
date
}
}
}
}
}
`
If I put a console.log of postQuery from inside the PostRepeater component, it comes back as undefined. So it appears that the component is never getting the data even though I followed the same layout from the tutorial above.
If I put a console.log('data', postQuery); from outside the component, I get the following in the console.
data 2575425095
What am I doing wrong?
After making a query gatsby injects it as an object matching the query.
In your case, as you see in GraphiQL you get an object with the initial key data.
{
"data": {
"allMarkdownRemark": {
"edges": [
{
"node": {
"frontmatter": {
"title": "...",
"date": ...
}
}
}
]
}
}
}
What you tried to do, is destructing a not existing key postQuery.
Moreover, you still can log it outside of the component scope as the value exported from export const postQuery
export const postQuery = graphql`
query MyQuery {
allMarkdownRemark {
edges {
node {
frontmatter {
path
title
date
}
}
}
}
}
`;
// v Logs the query id
console.log('my data:', postQuery);
// v Is not defined
export default function PostRepeater({ data, postQuery }) {
// v Shadowing the outter value of postQuery
console.log('my data:', postQuery);
// The query injected as "data" object
console.log('my data:', data);
return (
<>
// v Be aware of which object you query, edges is an array
{data.allMarkdownRemark.edges.map(...)}
</>
);
}
The export of graphql queries which gets automatically attached to props only work for page components. For any non-page components you need to use either the StaticQuery component or the useStaticQuery hook.
Read more here:
https://www.gatsbyjs.com/docs/static-query/
Here is how your code should look like using the useStaticQuery hook
import React from "react"
import "../styles/post-repeater.scss"
import { graphql, useStaticQuery, Link } from "gatsby"
export default function PostRepeater() {
const data = useStaticQuery(graphql`
query MyQuery {
allMarkdownRemark {
edges {
node {
frontmatter {
path
title
date
}
}
}
}
}
`)
// you might have multiple nodes in which case you must use filter
// or find to get node which contains your data.
const postQuery = data.allMarkdownRemark.edges[0].node.frontmatter;
return (
<>
{postQuery.map(instance =>
<div className="post">
<h2>{instance.title}</h2>
<span className="datePosted">{instance.date}</span>
<span className="readTime"></span>
<p></p>
<Link to={instance.path}>Read More</Link>
<hr />
</div>
)}
</>
)
}
I'm curretly following this tutorial on Meteor/Apollo/GraphQL, and having huge troubles to make a mutation with arguments/variables. Here is my code and some notes at the bottom !
The code
Schema
type Resolution {
_id: String!
name: String!
}
type Mutation {
createResolution(name: String!): Resolution
}
Resolver
import Resolutions from './resolutions'
export default {
Query: {
resolutions() {
return Resolutions.find({}).fetch()
}
},
Mutation: {
createResolution(obj, args, context) {
console.log('hey i get here')
}
}
}
The component using the mutation
import React, { Component } from 'react'
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
const createResolutionQuery = gql`
mutation createResolution($name: String!) {
createResolution(name: $name) {
_id
}
}
`
class ResolutionForm extends Component {
submitForm = () => {
this.props
.createResolution({
variables: {
name: this.name.value
}
})
.then(d => console.log('data received'))
.catch(e => console.log(e))
}
render() {
return (
<div>
<input type="text" ref={input => (this.name = input)} />
<button onClick={this.submitForm}>Submit</button>
</div>
)
}
}
export default graphql(createResolutionQuery, {
name: 'createResolution'
})(ResolutionForm)
What i know
When i try to send my query to the server, i get an http 400 error, and i get the following graphql error : "Unknown argument "name" on field "createResolution" of type "Mutation"."
The createResolution is available in my graphiQL but does not show any arguments in the doc.
It's stipulated in the tutorial that changing the .graphql schema does not trigger meteor server reloading, to apply change i have to modify my "register-api" file which is responsible for making the executable schema and create the apollo server with it. I made fake change to trigger it but it did not changed anything.
I tried to relaunch the server after erasing my browser's cache with no result.
So I think my problem is with the mutation arguments (brilliant deduction I know), but I can't figure out where is the typo or where I'm missing something. Help from somebody with a fresh look is welcome, thanks :)
Edit
Reinstall npm packages solved the issue.
All looks good I made a small change and added it as a pull request to your github repo.
createResolution(obj, {name}, context) {
console.log('hey i get here')
const id = Resolutions.insert({
name,
})
return Resolutions.findOne(id)
}
Running on my machine I get no errors.
I'm new to Relay and am having an issue with nested data on a fragment.
The following query returns the correct data when I test in it graphiql so I am confident my schema is correct.
{
viewer {
customers {
name
billing_address {
city
}
}
}
}
However, when I use the above query with Relay the customer.name will be correct but customer.billing_address.city is the same for every customer. It's confusing to me why some of the data would be correct while the nested data would just be copied.
I would appreciate any help with the issue and if anymore information is needed I would be glad to add it. Below is the only Relay code currently in my project and where I believe the issue may be.
class App extends Component {
render() {
console.log(this.props.viewer.customers);
return (
<div>
{this.props.viewer.customers.map((customer) => (
<div>
<div>{customer.name}</div>
<div>{customer.billing_address.city}</div>
</div>
))}
</div>
);
}
}
class AppHomeRoute extends Relay.Route {
static queries = {
viewer: () => Relay.QL`
query {
viewer
}
`,
};
static routeName = 'AppHomeRoute';
}
const AppContainer = Relay.createContainer(App, {
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
customers{
name
billing_address{
city
}
}
}`
},
});
ReactDOM.render(
<Relay.RootContainer
Component={AppContainer}
route={new AppHomeRoute()}
/>,
document.getElementById('root')
);
One possible problem could by how you resolve billing_address in your GraphQL schema. Did you include an field :id, !types.ID or globalIdField in your billing_address GraphQL type? Posting your GraphQL schema may also help.