Building CRUD Operations with TanStack Query
TanStack Query makes building CRUD (Create, Read, Update, Delete) operations seamless by providing tools for efficient data fetching, mutation handling, and cache management. This tutorial walks you through implementing CRUD operations in a React application using TanStack Query.
Prerequisites
Basic knowledge of React.
A REST API endpoint for demonstration (e.g., https://jsonplaceholder.typicode.com).
Install TanStack Query:
npm install @tanstack/react-query
npm install @tanstack/react-query-devtools
Step 1: Setting Up the Query Client
Before building CRUD operations, set up the QueryClient to manage queries and mutations.
App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { Posts } from './Posts';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<h1>TanStack Query CRUD Example</h1>
<Posts />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
Step 2: Fetching Data (READ Operation)
Use the useQuery hook to fetch and cache data.
Posts.js
import React from 'react';
import { useQuery } from '@tanstack/react-query';
const fetchPosts = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) throw new Error('Failed to fetch posts');
return response.json();
};
export function Posts() {
const { data, isLoading, isError, error } = useQuery(['posts'], fetchPosts);
if (isLoading) return <p>Loading posts...</p>;
if (isError) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Step 3: Creating Data (CREATE Operation)
Use the useMutation hook to handle POST requests and update the cache.
CreatePost.js
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const createPost = async (newPost) => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost),
});
if (!response.ok) throw new Error('Failed to create post');
return response.json();
};
export function CreatePost() {
const [title, setTitle] = useState('');
const queryClient = useQueryClient();
const mutation = useMutation(createPost, {
onSuccess: () => {
queryClient.invalidateQueries(['posts']); // Refetch posts
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ title });
setTitle('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter post title"
required
/>
<button type="submit">Create Post</button>
</form>
);
}
Step 4: Updating Data (UPDATE Operation)
Handle updates using useMutation and patch the data on the server.
UpdatePost.js
import React, { useState } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const updatePost = async ({ id, title }) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
});
if (!response.ok) throw new Error('Failed to update post');
return response.json();
};
export function UpdatePost({ id, currentTitle }) {
const [title, setTitle] = useState(currentTitle);
const queryClient = useQueryClient();
const mutation = useMutation(updatePost, {
onSuccess: () => {
queryClient.invalidateQueries(['posts']); // Refetch posts
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutation.mutate({ id, title });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
/>
<button type="submit">Update Post</button>
</form>
);
}
Step 5: Deleting Data (DELETE Operation)
Use useMutation for DELETE requests.
DeletePost.js
import React from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
const deletePost = async (id) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: 'DELETE',
});
if (!response.ok) throw new Error('Failed to delete post');
return id;
};
export function DeletePost({ id }) {
const queryClient = useQueryClient();
const mutation = useMutation(deletePost, {
onSuccess: () => {
queryClient.invalidateQueries(['posts']); // Refetch posts
},
});
return <button onClick={() => mutation.mutate(id)}>Delete Post</button>;
}
Step 6: Combining CRUD Operations
Integrate all components into a single view.
App.js
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Posts } from './Posts';
import { CreatePost } from './CreatePost';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<h1>CRUD with TanStack Query</h1>
<CreatePost />
<Posts />
</QueryClientProvider>
);
}
export default App;
Best Practices
- Invalidate Queries: Always invalidate queries to refresh cache after mutations.
- Error Handling: Use onError in mutations to gracefully handle errors.
- Optimistic Updates: Consider implementing optimistic updates for faster UI feedback.
In this tutorial, you’ve learned how to:
- Fetch and display data using useQuery.
- Create, update, and delete data using useMutation.
- Manage cache efficiently with query invalidation.
TanStack Query simplifies building robust CRUD operations with minimal effort. Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.
Comments
Post a Comment