Learned from this video: https://youtu.be/mPaCnwpFvZY
A hands-on learning project exploring TanStack Query (React Query) for server state management in React.
The core hook for fetching data from an API. It handles caching, background updates, and stale data automatically.
const { data, isPending, error, refetch } = useQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});useQuery provides several useful values to manage data fetching:
data— The fetched dataisPending— Loading state (true while fetching)error— Error object if the request failedrefetch— Function to manually trigger a refetch
Query keys uniquely identify cached data. TanStack Query uses these to determine when to refetch or serve cached data.
queryKey: ["todos"]; // Static key
queryKey: ["comments", id]; // Dynamic key - refetches when id changesThe queryFn is the async function that fetches your data. It should return a promise.
queryFn: async () => {
const response = await fetch("https://api.example.com/todos");
return response.json();
};Pass variables into the query key to filter/fetch specific data. The query automatically refetches when the key changes.
queryKey: ["comments", postId]; // Refetches when postId changesUse the enabled option to conditionally run queries. The query won't execute until enabled is true.
useQuery({
queryKey: ["comments", id],
queryFn: () => fetchComments(id),
enabled: isReady, // Only fetches when isReady is true
});Separate query configurations into dedicated files for reusability and cleaner components.
// queryOptions/createTodoQueryOptions.ts
export default function createTodoQueryOptions() {
return queryOptions({
queryKey: ["todos"],
queryFn: getAllTodos,
});
}
// App.tsx
const { data } = useQuery(createTodoQueryOptions());Guarantees data is fetched before the component renders. The data is never undefined, making it type-safe.
const { data } = useSuspenseQuery(queryOptions);
// data is guaranteed to exist (not undefined)Fetch multiple queries in parallel. Useful when you need to fetch several independent resources at once.
const results = useQueries({
queries: [
{ queryKey: ["todos"], queryFn: fetchTodos },
{ queryKey: ["users"], queryFn: fetchUsers },
],
});Chain queries that depend on previous results using enabled or combine with useSuspenseQuery.
// First query
const { data: user } = useQuery({ queryKey: ["user"], queryFn: fetchUser });
// Second query - only runs after user is fetched
const { data: posts } = useQuery({
queryKey: ["posts", user?.id],
queryFn: () => fetchPosts(user.id),
enabled: !!user, // Waits for user to exist
});src/
├── App.tsx # Main component with query examples
├── index.tsx # App entry with QueryClientProvider
└── queryOptions/ # Reusable query configurations
├── createTodoQueryOptions.ts
└── createCommentQueryOptions.ts
# Install dependencies
npm install
# Start development server
npm start