In my app I have a component called BlogFeed that expects an array of BlogPostEdge objects to be passed in via its posts prop. It looks something like this:
const BlogFeed = ({ posts, title }) => (
<div>
<h1>{title}</h1>
{posts.edges.map(edge => (
<BlogPostFragmentContainer key={edge.cursor} post={edge.node} />
))}
</div>
)
Right now I'm wrapping that in a fragment container:
export default createFragmentContainer(
BlogFeed,
graphql`
fragment BlogFeedFragmentContainer_posts on BlogPostConnection {
edges {
cursor
node {
...BlogPostFragmentContainer_post
}
}
}
`
)
And then finally populating that with the following root GraphQL query
query MainBlogFeedQuery($first: Int!, $cursor: String) {
blogPosts(first:$first, after:$cursor) {
...BlogFeedFragmentContainer_posts
}
}
I'd like to change this component's container to a PaginationContainer, but I've run into a few issues doing so. The root issue is that to use the @connection directive on my root query, I have to include an edges selection, or else I get an error from the Relay Compiler:
RelayConnectionTransform: Expected field `blogPosts: BlogPostConnection` to have a edges selection in document
But since I have an edges selection on my container's fragment, I can't do that. Including multiple edges selections results in my server only respecting the first one requested.
Is there an obvious solution I'm missing? Or failing that, some kind of workaround?
Currently @connection requires that the edges field be a direct child; using a connection where the only selection is a fragment spread isn't supported.
I would try moving the blogPosts field to the pagination container.
@josephsavona thanks! That totally fixed it for me
@rpowelll can you show your solution? I'm having the same issues.
@ajchambeaud sure thing! The pagination container essentially looks like this:
export default createPaginationContainer(
BlogFeed,
graphql`
fragment PaginatedBlogFeedContainer_feed on RootQueryType {
blogPosts(
first: $count,
after: $cursor
) @connection(key: "PaginatedBlogFeedContainer_blogPosts") {
edges {
cursor
node {
...BlogPostSummaryContainer_post
}
}
}
}
`,
{
query: graphql`
query PaginatedBlogFeedContainerQuery(
$count: Int!
$cursor: String
) {
...PaginatedBlogFeedContainer_feed
}
`,
getFragmentVariables(prevVars, totalCount) {
return {
...prevVars,
count: totalCount
}
},
getVariables(props, { count, cursor }, fragmentVariables) {
return {
count,
cursor
}
}
}
)
and then the root query container looks like this:
const MainBlogFeed = ({ location }) => {
const query: any = graphql`
query MainBlogFeedQuery($count: Int!, $cursor: String) {
...PaginatedBlogFeedContainer_feed
}
`
const variables = {
count: 5,
cursor: null
}
const render = ({ error, props, retry }) => {
if (error) {
return <div>{error.message}</div>
} else if (props) {
return <PaginatedBlogFeedContainer title="All Posts" feed={props} />
} else {
return <p>Loading…</p>
}
}
return (
<QueryRenderer
environment={environment}
query={query}
variables={variables}
render={render}
/>
)
}
@rpowelll zzz this was so hard to find. Why is this not mentioned on the Pagination page?!? How are we supposed to know that @connection requires that the edges field be a direct child...
Thanks.
@rpowelll Thanks for sharing your code snippets.
I have followed your snippets but got the error below
_Fragment "Products" cannot be spread here as objects of type "ProductConnection" can never be of type "Query"._
Looks like fragment Products can not be on Query which is root.
Any idea?
My relay version is 1.4.1 and see my code for reference.
index.js
import React, { Component } from 'react';
import { QueryRenderer, graphql } from 'react-relay';
import environment from 'Environment';
import Products from 'components/Main/Products';
const productsQuery = graphql`
query ProductsContainerQuery($count: Int!, $cursor: String) {
products {
...Products
}
}
`;
const variables = {
count: 10,
cursor: null
};
export default class ProductsContainer extends Component {
render() {
return (
<QueryRenderer
environment={environment}
query={productsQuery}
variables={{
count: 10,
cursor: null
}}
render={({ error, props }) => {
if (error) {
return <div>{error.message}</div>;
} else if (props) {
return <Products {...props} />;
}
return <div>Loading</div>;
}}
/>
);
}
}
import React, { Component } from 'react';
import { createPaginationContainer, graphql } from 'react-relay';
import { withRouter } from 'react-router-dom';
import { Container } from 'semantic-ui-react';
import Header from './Header';
import ProductsContainer from 'containers/Main/Products/Products';
class Products extends Component {
addProduct = e => this.props.history.push('/products/new');
render() {
const products = this.props.products.edges.map(x => ({
id: x.node.id,
price: x.node.price,
name: x.node.parrot.name,
supplier: x.node.supplier.name
}));
return (
<Container>
<Header addProduct={this.addProduct} />
<Container>
<ProductsContainer {...{ products }} />
</Container>
</Container>
);
}
}
export default createPaginationContainer(
withRouter(Products),
graphql`
fragment Products on Query {
products(first: $count, after: $cursor)
@connection(key: "Products_products", filters: []) {
edges {
cursor
node {
id
price
parrot {
id
name
description
}
supplier {
name
}
}
}
}
}
`,
{
query: graphql`
query ProductsQuery($count: Int!, $cursor: String) {
...Products
}
`,
getFragmentVariables(prevVars, totalCount) {
return {
...prevVars,
count: totalCount
};
},
getVariables(props, { count, cursor }, fragmentVariables) {
return {
count,
cursor
};
}
}
);
schema.graphql
...
type Query {
product(id: ID!): Product
products(skip: Int, after: String, before: String, first: Int, last: Int, supplierId: ID): ProductConnection
}
...
@DavidHe1127 the problem is here:
query: graphql`
query ProductsQuery($count: Int!, $cursor: String) {
...Products
}
`,
Instead, try this:
query: graphql`
query ProductsQuery($count: Int!, $cursor: String) {
products(first: $count, after: $cursor) {
...Products
}
}
`,
@jgcmarins
The bottom of the issue is in index.js
const productsQuery = graphql`
query ProductsContainerQuery($count: Int!, $cursor: String) {
products {
...Products
}
}
`;
needs to be changed to
const productsQuery = graphql`
query ProductsContainerQuery($count: Int!, $cursor: String) {
...Products
}
`;
But thanks for your comments anyway
+1 Can't use @connection in fragment, cannot use createPaginationContainer without @connection.
Most helpful comment
@ajchambeaud sure thing! The pagination container essentially looks like this:
and then the root query container looks like this: