0
votes

Summary: My problem is that when I create a node using a Relay mutation, the mutation creates the record in my database, but my client-side local data isn't updating, and the query response appears to be missing the new node.

Discussion: I think the RANGE_ADD should add the new node, ListingRating, to my local collection of ListingRatings, which should automatically update the connected nodes (Listing and User). However, according to the relay panel in react-dev-tools, the mutation doesn't include any of the fields I need updated.

Questions:

Should RANGE_ADD be sufficient here? If so, what's wrong with my implementation?

Do I have to update the Listing and User nodes manually? If so, how? Those nodes are only available deeply nested in the mutation payload, and FIELDS_CHANGE requires a fieldId in the top-level of the payload, right?

Schema: My schema holds Listings (adverts), Users, and Ratings (users' ratings of those Listings). it looks something like this:

type Listing = {
  ...
  ratings: [ListingRating]

}

type User = {
  ...
  ratings: [ListingRating]
}

type ListingRating = {
  rating: Int
  user: User
  listing: Listing
}

Mutation: I'm using scaphold.io as a back end, so don't have control over the mutation structures (though they're to Relay spec). They look like this:

mutation createListingRating(input: CreateListingRatingInput!) { CreateListingRatingPayload }

// CreateListingRatingInput
{
  listingId: ID
  listing: CreateListingInput
  rating: Int
  userId: ID
  user: CreateUserInput
  clientMutationId: ID
}

// CreateRatingPayload
{
  changedListingRating: ListingRating
  changedEdge: ListingRatingEdge
  viewer: Viewer
  clientMutationId: String
}

Relay mutation:

export default class CreateListingRating extends Relay.Mutation {

  getMutation() {
    return Relay.QL`
      mutation {
        createListingRating
      }
    `
  }

  getVariables() {
    return {
      userId: this.props.userId,
      listingId: this.props.listingId,
      rating: this.props.newRating,
    }
  }

  getFatQuery() {
    return Relay.QL`
      fragment on CreateListingRatingPayload @relay(pattern: true){
        changedEdge
        changedListingRating {
          listing
          user
          rating
        }
        viewer
      }
    `
  }

  getConfigs() {
    return [{
      type: 'RANGE_ADD',
      parentID: this.props.viewer.id,
      parentName: 'viewer',
      connectionName: 'allListingRatings',
      edgeName: 'changedEdge',
      rangeBehaviors: {
        '': 'append',
      },
    }]
  }

}

Query: (not sure this is relevant) In my app, the query I use to display the ListingRatings goes something like this:

query {
  getUserGroup(id: 'some-group-id') {
    users {
      edges {
        node {
          listings {
            edges {
              node {
                ratings {
                  edges {
                    node {
                      rating
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Mutation request: (ie, what displays in the relay panel in react-dev-tools)

mutation CreateListingRating($input_0:CreateListingRatingInput!) {

  createListingRating(input:$input_0) {

    ...F1
,

    clientMutationId
  }

}
fragment F0 on Viewer {

  id,

  user {

    firstName
,

    id
  }

}
fragment F1 on CreateListingRatingPayload {

  viewer {

    ...F0

  }

}
1

1 Answers

0
votes

I abandoned the method above and decided to go with the Relay.GraphQLMutation API, as suggested by the Relay docs to get ready for the Relay 2.0 API.

I can't say what the problem was in my OP, but conceptually, my problem was that I was adding a node to my UI, but failing to explicitly fetch the parent (ie, I was adding a ListingRating, but not updating the Listing, which, in my app, was the parent that rendered the ListingRating).

The github issue here &-one of the latest comments by nodzk were the ticket. here's my code:

import ListingRelayContainer from 'components/Listing

const Rating = (props, context) => {
  const _createListingRating = (input) => {
    createRating(input, context.relay)
  }
// ...render etc
}

// this API requires a `Relay Environment`, so pull 
// ours from context to avoid making anew
Rating.contextTypes = {
  relay: Relay.PropTypes.Environment,
}

const createRating = (variables, relayStore) => {
  const query = Relay.QL`mutation {
    createListingRating(input:$input) {
      changedListingRating {
        rating
        id
        listing {
           ${ListingRelayContainer.getFragment('listing')}
        }
      }
    }
  }
  `
  const input = { input: variables }
  const mutation = new Relay.GraphQLMutation(
    query,
    input,
    null, // No files.
    relayStore,
    {
      onFailure: err => console.warn(err),
      onSuccess: () => console.log('Success!'),
    },
  )
  mutation.commit()
}

Bing-bang-boom, all updated (though no optimistic update yet).

Notice we didn't even have to get into config (eg, RANGE_ADD). note that if we had wanted to fuss with config, we would have passed an object to mutation.commit().

The important part here is including the fragment from Listing. This ensures that the collection of ratings on the listing is updated to include the newly created Rating.