Mutations
Unlike queries, mutations in GraphQL are used to create, update or delete data.
For this purpose, Draqula offers a useMutation
hook.
Just like queries, remember to define your GraphQL queries outside your components to prevent infinite unnecessary rerenders.
Basic mutations
Assuming the server implements a ping
mutation, that simply returns "pong"
string, here's an example of the most basic mutation:
1 const PING_MUTATION = gql`2 mutation {3 ping4 }5 `;67 const PingPong = () => {8 const {mutate} = useMutation(PING_MUTATION);910 const onPing = async () => {11 const data = await mutate();12 // {13 // ping: 'pong'14 // }15 };1617 return <button onClick={onPing}>Ping</button>;18};
Mutations without variables are not that useful, so let's add some variables to closer match reality.
Variables
Similar to useQuery
, useMutation
also accepts variables.
The only difference is that you don't pass them to useMutation
itself, but to the mutate
function that it returns.
1 const CREATE_TODO_MUTATION = gql`2 mutation CreateTodo($title: String!) {3 createTodo(title: $title) {4 id5 title6 }7 }8 `;910const CreateTodo = () => {11 const [title, setTitle] = useState('');12 const {mutate} = useMutation(CREATE_TODO_MUTATION);1314 const onCreateTodo = async event => {15 // Prevent the form from refreshing the page16 event.preventDefault();1718 try {19 await mutate({title});2021 // Todo was successfully created22 } catch (error) {23 // Uh oh, something went wrong24 }25 };2627 return (28 <form onSubmit={onCreateTodo}>29 <input type="text" value={title} onChange={event => setTitle(event.target.value)} />30 <br />31 <button type="submit">Create Todo</button>32 </form>33 );34};
Caching
When a mutation succeeds, Draqula scans the GraphQL types returned from that mutation and refetches all queries that use any of these types. For example, let's say we have the following GraphQL schema on our server:
1 type Query {2 posts: [Post!]!3 pages: [Page!]!4 }56 type Mutation {7 createPost(title: String!): Post!8 }910type Post {11 title: String!12}1314type Page {15 title: String!16}
Imagine we also have the following code for our page:
1 const POSTS_QUERY = gql`2 query {3 posts {4 title5 }6 }7 `;89 const PAGES_QUERY = gql`10 query {11 pages {12 title13 }14 }15`;1617const CREATE_POST_MUTATION = gql`18 mutation CreatePost($title: String!) {19 createPost(title: $title) {20 title21 }22 }23`;2425const Blog = () => {26 const {data: posts} = useQuery(POSTS_QUERY);27 const {data: pages} = useQuery(PAGES_QUERY);28 const {mutate: createPost} = useMutation(CREATE_POST_MUTATION);2930 return (31 <>32 {/* ... */}33 <button onClick={() => createPost({title: 'Stranger Things'})}>Create Post</button>34 </>35 );36};
If a user clicks the "Create Post" button, the createPost
mutation will be executed, which returns only the Post
type.
That tells Draqula to find all queries that contain the Post
type and refetches them.
In this case, POSTS_QUERY
is the only one that uses this type, so it will be refetched, while PAGES_QUERY
will remain in the same state.
Refetch queries
However, sometimes a server uses the same data source but returns it under various types.
In that case, Draqula has no way to know that some query uses the same data under the hood and we need to help it find those queries to refetch.
This is where the refetchQueries
option comes in.
It's a list of queries to refetch in addition to the ones Draqula finds by itself.
So if in theory, PAGES_QUERY
needed to be refetched after the createPost
mutation, this is how it would work:
1const Blog = () => {2 const {data: posts} = useQuery(POSTS_QUERY);3 const {data: pages} = useQuery(PAGES_QUERY);4 const {mutate: createPost} = useMutation(CREATE_POST_MUTATION, {5 refetchQueries: [PAGES_QUERY]6 });78 // ...9};
It's important to mention that by default createPost
doesn't wait for queries to finish refetching.
If this behavior is important for you, you can opt-in for that via the waitForRefetchQueries
option:
1 const Blog = () => {2 const {data: posts} = useQuery(POSTS_QUERY);3 const {data: pages} = useQuery(PAGES_QUERY);4 const {mutate: createPost} = useMutation(CREATE_POST_MUTATION, {5 waitForRefetchQueries: true,6 refetchQueries: [PAGES_QUERY]7 });89 const onCreatePost = async () => {10 try {11 await createPost({title: 'Stranger Things'});1213 // Both POSTS_QUERY and PAGES_QUERY are refetched at this point14 } catch (error) {15 // ...16 }17 };1819 // ...20};
If you wish to disable refetching of any queries, set refetchQueries
parameter to false
:
1useMutation(CREATE_POST_MUTATION, {2 refetchQueries: false3});
API
useMutation(query, options?)
This hook returns a MutationResult
object.
query
Type: DocumentNode
Parsed GraphQL query via gql
function from the graphql-tag
module.
For example:
1const PING_MUTATION = gql`2 mutation {3 ping4 }5`;
options
Type: object
options.refetchQueries
Type: boolean | DocumentNode[]
Default: []
List of queries to refetch after the mutation succeeds.
Set to false
to disable any refetching.
options.waitForRefetchQueries
Type: boolean
Default: false
Determines whether to wait for queries to refetch or complete the mutation immediately.
MutationResult
The useMutation
hook returns an object consisting of the following fields.
mutate
Type: Function
A function that triggers a mutation. Accepts an optional object of variables to pass to the mutation. Returns a promise that resolves when the mutation is completed.
1const {mutate} = useMutation(CREATE_POST_MUTATION);23const onCreatePost = async () => {4 await mutate({5 title: 'New Post'6 });7};
data
Type: object
Data returned from the query.
isLoading
Type: boolean
Default: false
Indicates whether the mutation is currently in-flight.
error
Type: NetworkError | GraphQLError | undefined
The error returned from the mutation request. Read more in the "Error handling" section.