relay
relay copied to clipboard
Spread fragment through Connection
Bug
Invariant Violation: RelayConnectionTransform: Expected field `projects: ProjectNodeConnection` to have a edges selection in document `MenuQuery`
Relay compiler should notice that the fragment will spread with edges in the projects field, and pass without errors.
Code
Query:
query MenuQuery($unitId: ID!) {
unit(id: $unitId) {
id
projects(first: 10) @connection(key: "Unit_projects") {
...MenuProjectList_list
}
}
}
fragment:
fragment MenuProjectList_list on ProjectNodeConnection {
edges {
node {
...MenuProjectItem_item
}
}
}
Solution ?
I've tried many variations (trying to set the @connection within the fragment, trying to spread directly in unit instead of projects, etc. Nothing worked.
I use @connection a lot in order to update my lists after mutations. I'm unable to use fragments though, any suggestion to get fragments and @connection to play nicely until this relay-compiler bug gets fixed ? I will bypass my MenuProjectList component and directly call MenuProjectItem from my MenuQuery for now, but it lacks consistency with my other queries without @connection
I've had similar issues. You may want to look at your schema for a solution. I realized that I didn't understand some of the Types involved when fragmenting my queries. Also consider moving your edges/node blocks up into your MenuQuery query. I know visually things look correct but Relay is kinda strange in what it expects to find. While spreading the MenuProjectList in theory should expose edges { node { .... .... to the connection... I've found that it literally expects to see edges and not a child component being spread.
Here is a snippet from my codebase that may or may not be helpful. Renamed some things.
export default createPaginationContainer(
Table,
{
viewer: graphql`
fragment Table_viewer on Viewer {
allTables(
first: $count
after: $cursor
) @connection(key: "Table_allTables") {
edges {
node {
...Row
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
`,
},
export default createFragmentContainer(
Row,
graphql`
fragment Row on Table {
node1
node2
node3
etc....
}
`,
);
can we close this?
I think I need spreading fragment in connection work too. No idea how to reuse pagination component without it.
For example, when User and Collection both have posts connection field, a component that handles edges can't be shared in those parent components.
// If you can't spread this fragment in connections,
fragment PostsPagination_posts on PostConnection {
edges {
node {
id
}
}
}
// This code can't be DRYed.
fragment User_posts on User {
posts(
first: $count
) @connection(key: 'user_posts') {
edges {
node {
id
}
}
}
}
fragment Collection_posts on Collection {
posts(
first: $count
) @connection(key: 'Collection_posts') {
edges {
node {
id
}
}
}
}
In the case above, PostsPagination component cannot be reused.
https://github.com/facebook/relay/issues/1705#issuecomment-340220976
This is still a very valid issue especially if you want to use a union to return connection or errors at the same level. See: https://github.com/artsy/artsy.github.io/issues/495#issuecomment-465667460
It is not possible to use this directive even with custom handler @connection(handler: "..."). It's actually even described in the source code: https://github.com/facebook/relay/blob/ae057d84074bc5f0428f0c0f632b53464cebe924/packages/relay-compiler/handlers/connection/RelayConnectionTransform.js#L333-L336
Would you please consider adding a support for it? :)
What you can do for know is let your connection fragment have the @connection directive
like this:
fragment PostsPagination_posts on PostConnection @connection(key: "PostsPagination_posts", filters: []) {
edges {
node {
id
}
}
}
then you can use like this:
posts(first: 100) {
...PostsPagination_posts
}
does this approach solves your issue?
Yes, this is possible. So basically you have to have a root query spreading the fragment where you make the distinction between error and connection:
fragment LocationsPaginatedRefetch_data on DangerZone_RootQuery {
incrementalPagination: allLocations {
... on LocationConnection {
...LocationsPaginatedRefetch_test # <<< @connection inside
}
... on DangerZone_HTTPErrorType {
...HTTPError_error
}
}
}
And later have a fragment to handle the connection:
fragment LocationsPaginatedRefetch_test on LocationConnection
@connection(key: "allLocations_incrementalPagination") {
edges {
node {
id
...Location_location # <<< and optionally another fragment for the UI beauty :)
}
}
}
Correcto? Yeah, makes sense but still, it would be nice to be able to do it in one swoop. So let's take it as a feature request... 😊
Example taken from https://github.com/kiwicom/relay-example
@sibelius Have you tried the approach above? Note that RelayConnectionTransform only supports using @connection on a field, not a fragment definition.
The approach above is working well for us
Hmm, I would not expect that to work. @connection shouldn't be allowed on fragment definitions, that's a bug.
@josephsavona 😱 Do you have any advice for my problem, please? This was the only sane workaround I know. Others are: change GraphQL schema (I don't want), do not use @connection or add the support into Relay Compiler (which is both not that easy from my point of view).
You can manually do what the connection transform does:
- Use
@__clientField(handle: "connection")on the connection field to trigger the runtime to derive/update the value w ConnectionHandler, just as if you'd put@connectionon the field. - Manually fetch the relevant connection fields (pageInfo, edges.cursor, etc).
This is subject to change in upcoming releases so this isn't a great solution if you need to use this pattern widely, but for a one-off it would work while we identify a better solution.
@josephsavona So technically the connection handler works correctly in this situation and it's just disabled in Relay Compiler? I would be afraid to use the @__clientField directive directly but from your comment seems like there should be no problem with this. Thanks! :)
So technically the connection handler works correctly in this situation and it's just disabled in Relay Compiler
Generally speaking yes, though note that ConnectionHandler does make assumptions about the shape of the response field. You would probably have to build your own wrapper around ConnectionHandler to deal with the fact that your connection returns a Success/Error union.
@mrtnzlml Please share what you come up with! 🙏
2 sibelius
does this approach solves your issue?
in relay-compiler version 5.0.0 (and 6 as well) this is an error: Invalid directives @connection found on FRAGMENT_DEFINITION
I am struggling with the same problem. I have a Parent component that defines the connection and its arguments and I have multiple children components that have various data requirements towards that connection, but no claims on what the arguments are.
I want to do something like:
fragment Dashboard_viewer on Viewer {
routes(filterBy: $filterBy, first: 100) @connection(key: "Dashboard_routes", filters: ["filterBy"]) {
...Map_routes
...Timeline_routes
edges {
node {
id
name
}
}
}
}
fragment Map_routes on RouteConnection {
edges {
node {
id
directions
}
}
}
fragment Timeline_routes on RouteConnection {
edges {
node {
id
name
segments
}
}
}
This actually works, if anyone is wondering how to achieve connection level fragments.
@ivosabev Your example should work since there is an edges field directly on the connection. Do you get an error?
Connections are a difficult point to use fragment spreads, and we don't expect to remove the restriction on having a direct edges selection. In fact, we're exploring a new approach to representing connections that is more flexible in many ways - but to achieve that we will actually disallow fragment spreads on (new) connection fields. If you're trying to achieve a reusable connection component, you can generally do so by defining an edge fragment and passing the array of edges, pageInfo, and pagination callbacks (from PaginationContainer) to the connection component.
Huh, I've tried the example that was putting @connection() on the child fragments and got @MatrixNorm's error and assume that my example would not work, but it ACTUALLY does.
@josephsavona thanks for your response and clarification, maybe connection and edge level fragments should be documented with examples somewhere as they are somewhat hard to understand.
@ivosabev does that also work with having your Map and Timeline components as pagination containers? Or do you still have to hardcode the parent type (in this case Viewer) in connectionConfig.query?
I'm currently in the same boat as @taiki-t and have a connection that can have multiple parent types. Would be nice to have a reusable pagination container which I can use for both cases. In my case I have a SpeciesList component which paginates on the SpeciesConnection; which appears in Query.allSpecies but also in for example Query.genus(...).species.
@CrocoDillon
I have not tried. I am using simple fragment containers. My case is that I do not care what items are going to the child components. I just want to make sure they are the same and the data requirements of the children are clearly stated inside their definitions.
This seems to work now.
Ace, thanks for following-up @sorenhoyer :+1:
Sorry @alloy, I need to correct myself.
Specifying the edges field does indeed still seem to be required (at least in the latest experimental version), but if you want to use the @connection directive you can easily get around it by doing
edges {
__typename
}
Alternative as @josephsavona mentioned you can use @__clientField(handle: "connection") instead of @connection and leave out edges
In my opinion those 2 solutions are not that bad, but since @josephsavona mentions that they're looking into finding a better solution, perhaps this should be re-opened again.
Sorry for the false report!
Thanks for following up yet again 👍
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Seeing that this is now stale, wanted to check in to see if there are any plans to address this in an upcoming release so the workaround is no longer needed?
Still interest in this being addressed as I am running into all the above issues 7 years down the line 😅