Hi there,
I have just implemented a new feature in our product using Relay and it worked like a charm so far. Thank you for your awesome work and for open sourcing Relay!
Unfortunately I have encountered a tricky issue, that I currently do not know how to circumvent and if it is a bug in relay or if my schema is just programmed in a stupid way. Both options are valid at this point in time... :D
Situation
I have page showing a "Claim" that refers to a "Medium". This claim has an applied "Policy" that can be changed by a mutation. All these types implement a common Resource interface.
The shortened Schema looks like this.
interface Resource {
id: ID!
name: String
}
type Claim implements Node, Resource {
id: ID!
name: String
medium: Medium
policy: Policy
}
type Medium implements Node, Resource {
id: ID!
name: String
remoteUrl: String
}
type Policy implements Node, Resource {
id: ID!
name: String
}
The mutation is described as this:
type Mutation {
updateClaimPolicy(input: UpdatePolicyInput!): UpdatePolicyPayload
}
input UpdatePolicyInput {
id: ID!
policy: String!
clientMutationId: String!
}
type UpdatePolicyPayload {
claim: Claim
clientMutationId: String!
}
The client side mutation code:
export default class UpdateClaimPolicyMutation extends Relay.Mutation {
// we depend on the claim id, so make sure we get it
static fragments = {
claim: () => Relay.QL`
fragment on Claim {
id
}
`,
};
getMutation() {
return Relay.QL`mutation {updateClaimPolicy}`;
}
getVariables() {
return { id: this.props.claim.id, policy: this.props.policy._id };
}
/**
* All that could change...
* For now this is only the claims policy and the updater
*/
getFatQuery() {
return Relay.QL`
fragment on UpdatePolicyPayload {
claim {
policy
updatedOn
updatedBy
}
}
`;
}
// tell relay how to react to the payload...
// in this case, update the claim
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
claim: this.props.claim.id,
},
}];
}
}
The displaying container has the following (shortened) fragment:
claim: () => Relay.QL`
fragment on Claim {
_id
medium {
name
}
policy {
name
}
}
`,
So far everything is fine. I can navigate to the page and update the policy by the given mutation.
Problem
I have another page showing a list of occurred activities in the system.
An activity has a generic target that can be any type implementing the Resource interface.
type Activity implements Node {
id: ID!
time: DateTime
user: User
action: String
target: Resource
}
As I need to render the target differently depending on the type, I need to query for different data for different objects.
const mediumFragment = Relay.QL`
fragment on Medium {
name
remoteUrl
}
`;
const claimFragment = Relay.QL`
fragment on Claim {
medium {
${mediumFragment}
}
}
`;
export default Relay.createContainer(ActivityLine, {
fragments: {
activity: () => Relay.QL`
fragment on Activity {
id
time
user {
_id
fullName
}
action
target {
id
__typename
name
${mediumFragment}
${claimFragment}
}
}
`,
},
});
I need both fragments, as the target can either be a Medium directly or a Claim whos medium information I want to get as well.
This still works fine!
But when I show an activity with a claim (and expanded Medium) and then navigate to the claim edit-page (which still works fine btw) and execute the change policy mutation, I get the following error:
Fragment "F3" cannot be spread here as objects of type "Claim" can never be of type "Medium".
In the network tab, I see the request to the server asking for a Fragment on Medium, even though the medium is not in the mutation or its fat query.
"mutation UpdateClaimPolicyMutation($input_0:UpdatePolicyInput!){updateClaimPolicy(input:$input_0){clientMutationId,...F5}} fragment F0 on User{_id,fullName,id} fragment F1 on Claim{id} fragment F2 on Claim{policy{_id,name,id},id} fragment F3 on Medium{id} fragment F4 on Node{id} fragment F5 on UpdatePolicyPayload{claim{policy{_id,id},updatedBy{id,...F0},updatedOn,id,...F1,...F2,...F3,...F4,policy{_id,id},updatedBy{id,...F0},updatedOn,id}}"
Random Guessing
I guess the issue is that Relay is internally still tracking the combined queries from activities and the claim edit page and is somehow mixing up the requested fragments.
Of course a claim can never be a Medium! But I never asked to do that. I asked to get a Medium-Fragment on a Resource earlier, which might be a Claim. But not both at the same time of course.
So this seems kind of strange to me. Why would it try to apply the Medium Fragment on the Claim.
- It only happens when I load the activities first (my guess is tracking of the nodes/queries).
- And only when I make a mutation.
Querying the data works without any issue and making the mutation without accessing the activities first also works smoothly.
Do you have any possibilities to replicate this issue or have any comments how this can be avoided?
I wouldn't have a problem to simply "kill" tracked queries from my Activity-List page when I navigate away from it. I do not need any caching or anything else here. But I guess this is not the root-cause in this issue.
Thank you very much in advance for your support and kind regards,
Daniel