Closed staylor closed 4 years ago
what is the error?
Error: You supplied a GraphQL document with validation errors:
$ROOT/src/components/ContentNode/index.js:
Cannot spread fragment "ContentNode_content" within itself.
you could try to use @inline experimental directive to achieve this
The error is because the ContentNode_content
fragment is spread inside of itself. This is not allowed by the GraphQL spec: https://facebook.github.io/graphql/#sec-Fragment-spreads-must-not-form-cycles
If there was a cyclical relationship between ContentNode objects you would be requesting an infinite amount of data
@sibelius getting Invariant Violation: RelayParser: Unknown directive @inline
I am using Relay in modern mode, I see that directive in the docs, it also doesn't work if I try graphql.experimental
You need to install v1.2.0-rc.1 for @inline
to work. But I still don't think it will solve your issue as it's not allowed by the GraphQL spec because you are requesting potentially an infinite amount of data from your GraphQL server.
@femesq is doing something like this
the spreading is nice, was able to add it: https://github.com/staylor/wp-relay-app/blob/2d9d0f752e9764cbfdee9dc38f7216507c5aecb9/src/components/ContentNode/index.js
Another thing to note: this is reallly an opaque structure (HTML parsed into JSON) - there is never a time when I only want some of it.
For my use-case, I've opted to "fetch" the first 4 levels of my tree, and their children are 'fetched' on demand using PaginationContainers... This can lead to many query cascading, depending on the user's tree-structure, but this approach solves the most of user's arrangement fine...
you can solve this using a QueryRenderer
QueryRenderer example, remove the toggle control and it'll fire off as many queries as required to load the whole tree, which is probably a bad idea.
import React, {Component} from 'react'
import Immutable from 'immutable'
import { createFragmentContainer, graphql, QueryRenderer } from 'react-relay'
const CategoryTreeQuery = graphql`
query CategoryTreeQuery(
$parent: Int!
) {
query {
...CategoryTree_query
}
}
`;
class CategoryTree extends Component {
state = {
expanded: Immutable.Set()
}
toggle = (e, id) => {
e.preventDefault();
const has = this.state.expanded.includes(id)
const expanded = has ? this.state.expanded.delete(id) : this.state.expanded.add(id)
this.setState({expanded})
}
render() {
const { query: { categories }, relay } = this.props
const { expanded } = this.state
return categories.edges ? (
<ul>
{categories.edges.map(({ node: category }) => (
<li key={category.id}>
<a onClick={(e) => this.toggle(e, category.id)}>{category.name}</a>
{expanded.includes(category.id) &&
<QueryRenderer
environment={relay.environment}
query={CategoryTreeQuery}
variables={{
parent: category.term_id
}}
render={({ error, props }) => {
if (error) {
return <div>{error.message}</div>
} else if (props) {
return <CategoryTreeRelay {...props} />
}
return <div>Loading</div>
}}
/>
}
</li>
)
)}
</ul>
) : null
}
}
const CategoryTreeRelay = createFragmentContainer(
CategoryTree,
graphql`
fragment CategoryTree_query on Query {
categories: terms (
first: 1000
parent: $parent
) {
edges {
node {
id
term_id
name
}
}
}
}
`
)
export default CategoryTreeRelay
My example is about representing HTML as a tree structure that can have deep nesting. It is just a field, not a paginated result set.
<section>
<header>
<h1>
<strong>Warning:</strong> This can get deeply nested. <a href="">Even in <span>Here,
<em>riiiight</em>.</span>.</a>
</h1>
</header>
<p>More Text</p>
</section>
There is no way I only want some levels of this data, I always need all of it. In the GQL query, assuming all nodes have a type, I need to do this to reach em
:
node {
tagName // section
children {
tagName // header
children {
tagName // h1
children {
tagName // a
children {
tagName // span
children {
tagName // em
children {
text
... this could go on for many more levels in a table or something
}
}
}
}
}
}
}
It would nice if recursion could be introduced somewhere in here - the problem lies with children
being a list of the same type as the item including it
@staylor try to follow @tim-field answer https://github.com/facebook/relay/issues/1998#issuecomment-330367298
that idea is to only call another query renderer if needed
the other way to resolve this, is to change the resolvers structure
GraphQL doesn't support recursive fragments by design, as this could allow unbounded data-fetching. Relay goes a bit further and offers support for recursion, but it still has to be terminated - you can use @argumentDefinitions
to define a boolean value that is used to conditionally include the same fragment, passing @arguments
to change the condition. But the recursion still has to terminate statically - e.g. you can have a fixed number of levels of recursion.
I'm going to close as this is an intentional restriction that we don't plan on changing.
I would like to spread a fragment within a fragment, but this is disallowed by the compiler:
See full code here: https://github.com/staylor/wp-relay-app/blob/master/src/components/ContentNode/index.js
In lieu of spreading the fragment, I have recursively included nodes at a limited depth. How might this be accomplished with static queries?
The SDL is: