Custom useMutation Hook
In this lesson, we'll create a custom useMutation Hook which will abstract the server fetch functionality needed to conduct a mutation from a component.
We've created a
useQuery Hook to help make a GraphQL query request when a component first mounts. In this lesson, we'll look to create a
useMutation Hook that helps prepare a function to make an API mutation request.
useMutation Hook will behave differently since we won't want a mutation to run the moment a component mounts by default. Our
useMutation Hook will simply receive the mutation query document to be made.
And return an array of two values - the first being the request function and the second being an object that contains detail of the request.
We'll get a better understanding of how our
useMutation Hook is to behave once we start to create it.
We'll create our
useMutation Hook in a file of its own in the
src/lib/api folder. We'll label this file
Similar to the
useQuery Hook, we're going to need to keep track of some state within our
useMutation Hook so we'll import the
useState Hook. Since we're going to be interacting with the server as well, we'll import the
server object which will help us make the
We'll create a
State interface that describes the shape of the state object we'll want to maintain. The
State interface will have the
error fields. The type of
data will either be equal to a type variable passed in the interface (
error fields will be of type
We'll export and create a
const function called
useMutation function will accept two type variables -
TData is to represent the shape of data that can be returned from the mutation while
TVariables is to represent the shape of variables the mutation is to accept. Both of the
TVariables type variables will have a default type value of
Our mutation function, however, will only accept a single required document
queryhere is used to reference the GraphQL request that is to be made. One can rename the
mutationto be more specific.
We expect variables necessary for our request to be used in our
useMutation Hook but we haven't specified
variables as a potential argument in our function. The reason being is how we want our Hook to work. In our use case, we won't want to pass in the variables when we use the
useMutation Hook but instead pass it in the request function the mutation is expected to return.
Let's go through an example of what we intend to do. Assume the
useMutation Hook when used in a component is to return a fetch function that we'll label as
Only when the
request function is called, will we pass in the variables necessary for the mutation.
This is simply how we want to set up our
useMutation Hook. It's possible to also pass in variables when we run the Hook function as well.
At the beginning of the
useMutation Hook, we'll initialize the state object we'll want our Hook to maintain. We'll initialize our state similar to how we've done in the
useQuery Hook by setting
null and the
error fields to
We'll now create a
fetch() function in our
useMutation Hook that will be responsible for making our request.
fetch() will be an asynchronous function that accepts a
variable object that will have a type equal to the
TVariables type variable. We'll also state the
variables parameter is an optional parameter since there may be mutations we can create that don't require any variables.
We'll introduce a
try/catch statement within the
At the beginning of the
try block, we'll set the state
loading property to
true since at this point the request will be in-flight. We'll keep
error as the original values of
We'll then conduct our
server.fetch() function, and pass in the
variables values the
server.fetch() function can accept. The
server.fetch() function will return an object of
errors so we'll destruct those values as well. We'll also pass along the type variables of
variables to ensure the information returned from the
server.fetch() function is appropriately typed.
In the last lesson, we observed how GraphQL requests could resolve but have errors be returned from our resolver. These errors will be captured in the
errors array we'retrieving from the
server.fetch() function. We'll check to see if these errors exist and if so - throw an
Error and pass in the error message from the first error object in the
If the request is successful and no errors exist, we'll set the returned
data into our state. We'll also set the
error properties to false.
If an error arises for either the request failing or errors being returned from the API, we'll set the
error value in our state to
true. We'll also specify
data should be
loading is false. We'll capture the
error message and look to log the
error message in the browser console.