Mutations
Create a mutation
Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. You can use MutationBuilder
or useMutation
to create a mutation.
- Vanilla
- Flutter Hooks
MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 5),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
builder: (context, mutation) {
/* ... */
},
);
final mutation = useMutation<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 1),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
)
MutationBuilder
or useMutation
requires 4 type arguments to provide proper type intellisense and type-safety. This might seem overwhelming but at the end of the day saves time and effort. The type arguments are as follows in order:
DataType
- The type of the data returned by the mutation functionErrorType
- The type of the error returned by the mutation functionVariablesType
- The type of the variables passed to the mutation functionRecoveryType
- The type of the data returned by the onMutate callback and to be passed on onSuccess & onError callback. Which can be used to recover from an error and continue the mutation flow.
Callbacks
MutationBuilder
or useMutation
comes with some helper parameters that allow quick and easy side-effects at any stage during the mutation lifecycle. These come in handy for both invalidating and refetching queries after mutations and even optimistic updates
- Vanilla
- Flutter Hooks
MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, String>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 5),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
onMutate: (variables) {
print('onMutate: $variables');
return "Recover ME";
},
onData: (data, recoveryData) {
print('onData: $data');
print('recoveryData: $recoveryData');
},
onError: (error, recoveryData) {
print('onError: $error');
print('recoveryData: $recoveryData');
},
builder: (context, mutation) {
/* ... */
},
);
final mutation = useMutation<Map<String, dynamic>, dynamic, Map<String, dynamic>, String>(
'sign-up',
(variables) {
return Future.delayed(
const Duration(seconds: 1),
() => {
'name': variables['name'],
'email': variables['email'],
'password': variables['password'],
},
);
},
onMutate: (variables) {
print('onMutate: $variables');
return "Recover ME";
},
onData: (data, recoveryData) {
print('onData: $data');
print('recoveryData: $recoveryData');
},
onError: (error, recoveryData) {
print('onError: $error');
print('recoveryData: $recoveryData');
},
)
Learn how to use
onMutate
&Query.setData
to implement optimistic updates
Refetch Queries and InfiniteQueries on successful mutation
- Vanilla
- Flutter Hooks
MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
/* ... */,
refreshQueries: const ['user-profile'],
refreshInfiniteQueries: const ['feeds'],
)
final mutation = useMutation<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up',
/* ... */,
refreshQueries: const ['user-profile'],
refreshInfiniteQueries: const ['feeds'],
)
Mutation
The MutationBuilder
/useMutation
returns a Mutation
object that can be used to trigger the mutation and access the state of the mutation.
States
A mutation can only be in one of the following states at any given moment:
isInactive
- The mutation is currently idle or in a fresh/reset stateisMutating
- The mutation is currently running and performing the mutation
Beyond those primary states, more information is available depending on the state of the mutation:
hasError
- If the mutation is in an error state, the error is available via theerror
property.hasData
- If the mutation is in a success state, the data is available via thedata
property.
Performing a mutation
You can use the Mutation.mutate
method to trigger your mutation. The mutate
method accepts a single variable or object as an argument. This variable or object will be passed to your mutation function.
await mutation.mutate({
'name': 'John Doe',
'email': 'john.doe@mail.com'
'password': 'password',
})
The variables must be the type specified as
VariableType
inMutationBuilder
oruseMutation
.
All mutations are by default asynchronous and immediately returns a Future
with available data. But if you want to
schedule the mutation in queue and wait for the result, you need to pass the scheduleToQueue: true
parameter to the
mutate
method
await mutation.mutate(
{
'name': 'John Doe',
'email': 'john.doe@mail.com'
'password': 'password',
},
scheduleToQueue: true,
)
Resetting a mutation
You can use the Mutation.reset
method to reset the mutation to its initial state
await mutation.reset();
Dynamic key
You can use Dart's string interpolation to dynamically generate the key for a mutation. This is useful when you want separate mutations that has same data type but are triggered by different events.
- Vanilla
- Flutter Hooks
MutationBuilder<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up?provider=$authProvider',
(variable) => auth.signUp(variable, provider: authProvider),
builder: (context, mutation) {
/* ... */
},
)
final mutation = useMutation<Map<String, dynamic>, dynamic, Map<String, dynamic>, dynamic>(
'sign-up?provider=$authProvider',
(variable) => auth.signUp(variable, provider: authProvider),
)
For every
authProvider
a new mutation will be created and will be cached separately. Which are also isolated from each other.
Btw,
authProvider
andauth.signUp
are imaginary variables and methods