Handling Mutations with TanStack Query
In TanStack Query, mutations are used for modifying server-side data, such as creating, updating, or deleting records. The useMutation hook simplifies the process of handling these operations and provides tools for managing loading states, errors, and cache updates.
This tutorial will guide you through:
- Setting up mutations.
- Updating cache after mutations.
- Implementing optimistic updates.
Step 1: Setting Up the Query Client
Before working with mutations, initialize the QueryClient to manage queries and mutations in your app.
Install TanStack Query
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
Setup QueryClient
App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { CreateUser } from './CreateUser';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<h1>Handling Mutations with TanStack Query</h1>
<CreateUser />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
Step 2: Creating Data with Mutations
Use the useMutation hook to handle POST requests and modify server-side data.
Example: Creating a User
CreateUser.js
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const createUser = async (newUser) => {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUser),
});
if (!response.ok) throw new Error('Failed to create user');
return response.json();
};
export function CreateUser() {
const [name, setName] = useState('');
const queryClient = useQueryClient();
const mutation = useMutation(createUser, {
onSuccess: () => {
// Invalidate the 'users' query to fetch the updated data
queryClient.invalidateQueries(['users']);
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ name });
setName('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter user name"
required
/>
<button type="submit" disabled={mutation.isLoading}>
{mutation.isLoading ? 'Creating...' : 'Create User'}
</button>
{mutation.isError && <p>Error: {mutation.error.message}</p>}
</form>
);
}
Step 3: Updating Data with Mutations
To update server-side data, use the useMutation hook with PUT or PATCH requests.
Example: Updating User Data
UpdateUser.js
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const updateUser = async ({ id, name }) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
});
if (!response.ok) throw new Error('Failed to update user');
return response.json();
};
export function UpdateUser({ id, currentName }) {
const [name, setName] = useState(currentName);
const queryClient = useQueryClient();
const mutation = useMutation(updateUser, {
onSuccess: () => {
queryClient.invalidateQueries(['users']); // Refresh the 'users' query
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ id, name });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<button type="submit" disabled={mutation.isLoading}>
{mutation.isLoading ? 'Updating...' : 'Update User'}
</button>
{mutation.isError && <p>Error: {mutation.error.message}</p>}
</form>
);
}
Step 4: Deleting Data with Mutations
Handle DELETE requests to remove records from the server.
Example: Deleting a User
DeleteUser.js
import React from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const deleteUser = async (id) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
method: 'DELETE',
});
if (!response.ok) throw new Error('Failed to delete user');
return id;
};
export function DeleteUser({ id }) {
const queryClient = useQueryClient();
const mutation = useMutation(deleteUser, {
onSuccess: () => {
queryClient.invalidateQueries(['users']); // Refresh the 'users' query
},
});
return (
<button onClick={() => mutation.mutate(id)} disabled={mutation.isLoading}>
{mutation.isLoading ? 'Deleting...' : 'Delete User'}
</button>
);
}
Step 5: Optimistic Updates
Optimistic updates allow you to update the UI immediately, before the mutation completes, for better user experience.
Example: Optimistic Update for Deleting a User
DeleteUser.js
const mutation = useMutation(deleteUser, {
onMutate: async (id) => {
await queryClient.cancelQueries(['users']); // Cancel ongoing queries
const previousUsers = queryClient.getQueryData(['users']); // Snapshot current cache
// Optimistically update cache
queryClient.setQueryData(['users'], (old) =>
old.filter((user) => user.id !== id)
);
return { previousUsers }; // Return context
},
onError: (error, id, context) => {
queryClient.setQueryData(['users'], context.previousUsers); // Rollback on error
},
onSettled: () => {
queryClient.invalidateQueries(['users']); // Refetch users
},
});
Step 6: Putting It All Together
Combine the Create, Update, and Delete components in a single application.
App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { CreateUser } from './CreateUser';
import { UpdateUser } from './UpdateUser';
import { DeleteUser } from './DeleteUser';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<h1>TanStack Query: Handling Mutations</h1>
<CreateUser />
{/* Add UpdateUser and DeleteUser components here */}
</QueryClientProvider>
);
}
export default App;
Best Practices
- Invalidate Queries: Always invalidate or update the cache after successful mutations.
- Error Handling: Use onError to manage mutation errors.
- Optimistic Updates: Improve user experience by reflecting changes instantly.
- Separation of Concerns: Keep fetching (queries) and updating (mutations) logic separate.
In this tutorial, you learned how to:
- Handle Create, Update, and Delete operations using useMutation.
- Invalidate or update cached data after mutations.
- Implement optimistic updates for better UI responsiveness.
These techniques make TanStack Query a robust choice for managing server-side state in modern applications. Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.
Comments
Post a Comment