Joon
Joon

Reputation: 9914

Relay mutation on plain array is not working

I am having a hard time figuring out how to do mutations on plain array via relay.

I am trying to add a new tag to a post. It does not get updated on client-side after being successfully added on the server-side.

I have to manually reload to see the new tag. I have tried both REQUIRED_CHILDREN and this.props.relay.forceFetch(), but to no avail.

Also, tried FIELDS_CHANGE for post.

GraphQL Schema:

Post {
  id: ID!
  text: String!
  tags: [Tag!]!
}

Tag {
  id: ID!
  name: String!
}

AddTagToPostMutation:

  static fragments = {
    post: () => Relay.QL`
      fragment on Post {
        id
        tags
      }
    `,
  }

  getMutation() {
    return Relay.QL`mutation { addTagToPost }`;
  }

  getVariables() {
    return {
      name: this.props.tag.name,
    };
  }

  getFatQuery() {
    return Relay.QL`
      fragment on AddTagToPostMutationPayload {
        tag {
          id
          name
        }
        post {
          id
          tags
        }
      }
    `;
  }

  getConfigs() {
    return [{
      type: 'REQUIRED_CHILDREN',
      children: [Relay.QL`
        fragment on AddTagToPostMutationPayload {
          tag {
            id
            name
          }
          post {
            id
            tags
          }
        }
      `],
    }];
  }

  getOptimisticResponse() {
    return {
      tag: {
        name: this.props.tag.name,
      },
      post: {
        id: this.props.post.id,
      },
    };
  }

Upvotes: 1

Views: 832

Answers (2)

Ahmad Ferdous
Ahmad Ferdous

Reputation: 3399

As freiksenet already pointed out, FIELDS_CHANGE should be used in getConfigs() function. I took your schema, implemented the GraphQL types, server-side and client-side mutation to add tag to a post. The client-side gets updated successfully. I'm going to just elaborate the solution in my answer.

First, check your server-side mutation. My implementation uses graphql and graphql-relay libraries and looks like below. Notice that the output of the server-side mutation is a post to which a tag has been added. This post is the one whose ID was provided as input.

const AddTagToPostMutation = mutationWithClientMutationId({
  name: 'AddTagToPost',
  inputFields: {
    postId: { type: new GraphQLNonNull(GraphQLID) },
    name: { type: new GraphQLNonNull(GraphQLString) },
  },
  outputFields: {
    post: {
      type: PostType,
      resolve: ({id}) => getPost(id),
    },
  },
  mutateAndGetPayload: ({postId, name}) => {
    const id = fromGlobalId(postId).id;
    addTagToPost(id, name);
    return {id};
  },
});

Using graphiql, you can test your mutation:

mutation {
  addTagToPost(input:{
      postId: "UG9zdDpwb3N0Mg==" 
      name:"a new tag name" 
      clientMutationId:"123244"}) {
    post {
      id
      text
      tags {
        id
        name
      }
    }
  }
}

I added a field posts for all posts to the root query. Using graphiql, I first checked the post IDs and used one above.

Using react-relay, the client-side mutation code looks like below. It is passed a prop post whose ID is used as input variable in getVariables() function. In the getConfigs() function, we specify that post field has to be updated. The association between the payload field post and the passed prop post is established using FIELDS_CHANGE mutation type.

export default class AddTagToPostMutation extends Relay.Mutation {
  getMutation() {
    return Relay.QL`mutation{addTagToPost}`;
  }

  getVariables() {
    return {
      postId: this.props.post.id,
      name: this.props.name,
    };
  }

  getFatQuery() {
    return Relay.QL`
      fragment on AddTagToPostPayload {
        post {
          id,
          tags {
            id,
            name,
          }
        }
      }
    `;
  }

  getConfigs() {
    return [{
      type: 'FIELDS_CHANGE',
      fieldIDs: {
        post: this.props.post.id,
      },
    }];
  }

  static fragments = {
    post: () => Relay.QL`
      fragment on Post {
        id,
      }
    `,
  };
}

The client-side mutation is invoked like this:

Relay.Store.commitUpdate(new AddTagToPostMutation({
      post: postToModify,
      name: tagName,
    }));

Upvotes: 3

freiksenet
freiksenet

Reputation: 3679

I think you should just use FIELDS_CHANGE in such situations.

  getConfigs() {
    return [{
      type: 'FIELDS_CHANGE',
      fieldIDs: {post: this.props.post.id},
    }];
  }

  getOptimisticResponse() {
    return {
      post: {
        id: this.props.post.id,
        tags: [...this.props.post.tags, this.props.tag],
      },
    };
  }

Upvotes: 1

Related Questions