Relay.Mutation
Relay makes use of GraphQL mutations; operations that enable us to mutate data on the client and server. To create a mutation for use in our app, we subclass Relay.Mutation
and implement, at minimum, the four abstract methods listed below.
Overview
Properties
-
static fragments
Declare this mutation's data dependencies here -
static initialVariables
A default set of variables to make available to this mutation's fragment builders -
static prepareVariables
A method to modify the variables based on the runtime environment, previous variables, or the meta route
Methods
-
constructor(props)
-
abstract getConfigs()
-
abstract getFatQuery()
-
abstract getMutation()
-
abstract getVariables()
-
static getFragment(fragmentName[, variableMapping])
-
getCollisionKey()
-
getFiles()
-
getOptimisticConfigs()
-
getOptimisticResponse()
Properties
fragments (static property)
static fragments: RelayMutationFragments<$Keys<Tp>>
// Type of RelayMutationFragments
type RelayMutationFragments<Tk> = {
[key: Tk]: FragmentBuilder;
};
// Type of FragmentBuilder
type FragmentBuilder = (variables: Variables) => RelayConcreteNode;
We declare our mutations' data dependencies here, just as we would with a container. This is particularly useful to ensure that a set of fields we might want to use in this mutation's optimistic response have been fetched.
Example
class LikeStoryMutation extends Relay.Mutation {
static fragments = {
story: () => Relay.QL`
fragment on Story {
likers { count },
viewerDoesLike,
}
`,
};
getOptimisticResponse() {
// this.props.story.likers.count and this.props.story.viewerDoesLike
// are guaranteed to have been fetched since we've declared
// them to be part of this mutation's data dependencies above.
return { /* ... */ };
}
}
See also: Mutations > Fragment variables and Mutations > Optimistic updates
initialVariables (static property)
static initialVariables: {[name: string]: mixed};
The defaults we specify here will become available to our fragment builders:
Example
class ChangeTodoStatusMutation extends Relay.Mutation {
static initialVariables = {orderby: 'priority'};
static fragments = {
todos: () => Relay.QL`
# The variable defined above is available here as $orderby
fragment on User { todos(orderby: $orderby) { ... } }
`,
};
/* ... */
}
See also: Mutations > Fragment variables
prepareVariables (static property)
static prepareVariables: ?(
prevVariables: {[name: string]: mixed},
route: RelayMetaRoute,
) => {[name: string]: mixed}
// Type of `route` argument
type RelayMetaRoute = {
name: string;
}
If we provide to a mutation a method that conforms to the signature described above, it will be given the opportunity to modify the fragment builders' variables, based on the previous variables (or the initialVariables
if no previous ones exist), the meta route, and the runtime environment. Whatever variables this method returns will become available to this mutation's fragment builders.
Example
class BuySongMutation extends Relay.Mutation {
static initialVariables = {format: 'mp3'};
static prepareVariables = (prevVariables) => {
var overrideVariables = {};
var formatPreference = localStorage.getItem('formatPreference');
if (formatPreference) {
overrideVariables.format = formatPreference; // Lossless, hopefully
}
return {...prevVariables, overrideVariables};
};
/* ... */
}
See also: Mutations > Fragment variables
Methods
constructor
Create a mutation instance using the new
keyword, optionally passing it some props. Note that this.props
is not available inside the constructor function, but are set for all the methods mentioned below (getCollisionKey
, getOptimisticResponse
, etc). This restriction is due to the fact that mutation props may depend on data from the RelayEnvironment, which isn't known until the mutation is applied with applyUpdate
or commitUpdate
.
Example
var bookFlightMutation = new BuyPlaneTicketMutation({airport: 'yvr'});
Relay.Store.commitUpdate(bookFlightMutation);
getConfigs (abstract method)
abstract getConfigs(): Array<{[key: string]: mixed}>
Implement this required method to give Relay instructions on how to use the response payload from each mutation to update the client-side store.
Example
class LikeStoryMutation extends Relay.Mutation {
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
story: this.props.story.id,
},
}];
}
}
See also: Mutations > Mutator configuration
getFatQuery (abstract method)
abstract getFatQuery(): GraphQL.Fragment
Implement this required method to design a ‘fat query’ – one that represents every field in your data model that could change as a result of this mutation.
Example
class BuySongMutation extends Relay.Mutation {
getFatQuery() {
return Relay.QL`
fragment on BuySongPayload {
songs {
count,
edges,
},
totalRunTime,
}
`,
}
}
See also: Mutations > The fat query
getMutation (abstract method)
abstract getMutation(): GraphQL.Mutation
Implement this required method to return a GraphQL mutation operation that represents the mutation to be performed.
Example
class LikeStoryMutation extends Relay.Mutation {
getMutation() {
return this.props.story.viewerDoesLike
? return Relay.QL`mutation {unlikeStory}`
: return Relay.QL`mutation {likeStory}`;
}
}
getVariables (abstract method)
abstract getVariables(): {[name: string]: mixed}
Implement this required method to prepare variables to be used as input to the mutation.
Example
class DestroyShipMutation extends Relay.Mutation {
getVariables() {
return {
// Assume that the server exposes a `destroyShip` mutation
// that accepts a `shipIDToDestroy` variable as input:
shipIDToDestroy: this.props.ship.id,
};
}
}
Warning
The term ‘variables’ here refers to the input to the server-side mutation, not to the variables made available to this mutation's fragment builders.
getFragment (static method)
static getFragment(
fragmentName: $Keys<Tp>,
variableMapping?: Variables
): RelayFragmentReference
// Type of the variableMapping argument
type Variables = {[name: string]: mixed};
Gets a fragment reference for use in a parent's query fragment.
Example
class StoryComponent extends React.Component {
/* ... */
static fragments = {
story: () => Relay.QL`
fragment on Story {
id,
text,
${LikeStoryMutation.getFragment('story')},
}
`,
};
}
You can also pass variables to the mutation's fragment builder from the outer fragment that contains it.
class Movie extends React.Component {
/* ... */
static fragments = {
movie: (variables) => Relay.QL`
fragment on Movie {
posterImage(lang: $lang) { url },
trailerVideo(format: $format, lang: $lang) { url },
${RentMovieMutation.getFragment('movie', {
format: variables.format,
lang: variables.lang,
})},
}
`,
};
}
Hint
In that last example, think of
$format
andvariables.format
as the same value.
getCollisionKey
getCollisionKey(): ?string
Implement this method to return a collision key. Relay will send any mutations having the same collision key to the server serially and in-order.
Example
class LikeStoryMutation extends Relay.Mutation {
getCollisionKey() {
// Give the same key to like mutations that affect the same story
return `like_${this.props.story.id}`;
}
}
getFiles
getFiles(): ?FileMap
// Type of the FileMap object
type FileMap = {[key: string]: File};
Implement this method to return a map of File
objects to upload as part of a mutation.
Example
class AttachDocumentMutation extends Relay.Mutation {
getFiles() {
return {
file: this.props.file,
};
}
}
class FileUploader extends React.Component {
handleSubmit() {
var fileToAttach = this.refs.fileInput.files.item(0);
Relay.Store.commitUpdate(
new AttachDocumentMutation({file: fileToAttach})
);
}
}
getOptimisticConfigs
getOptimisticConfigs(): Array<{[key: string]: mixed}>
Implement this method in cases where the mutator configuration needed to handle the optimistic response needs to be different than the one that handles the server response.
See also: Relay.Mutation::getConfigs()
getOptimisticResponse
getOptimisticResponse(): ?{[key: string]: mixed}
Implement this method to craft an optimistic response having the same shape as the server response payload. This optimistic response will be used to preemptively update the client cache before the server returns, giving the impression that the mutation completed instantaneously.
Example
class LikeStoryMutation extends Relay.Mutation {
getOptimisticResponse() {
return {
story: {
id: this.props.story.id,
likers: {
count: this.props.story.likers.count + 1,
},
viewerDoesLike: !this.props.story.viewerDoesLike,
},
};
}
}
See also: Mutations > Optimistic updates