// src/hooks/useTodos.ts import { useQuery, queryOptions } from '@tanstack/react-query' /** * Type definitions */ export type Todo = { id: number title: string completed: boolean userId: number } /** * API function - keeps network logic separate */ async function fetchTodos(): Promise { const response = await fetch('https://jsonplaceholder.typicode.com/todos') if (!response.ok) { throw new Error(`Failed to fetch todos: ${response.statusText}`) } return response.json() } /** * Query options factory (v5 best practice) * * Benefits: * - Reusable across useQuery, useSuspenseQuery, prefetchQuery * - Perfect type inference * - Single source of truth for queryKey and queryFn */ export const todosQueryOptions = queryOptions({ queryKey: ['todos'], queryFn: fetchTodos, staleTime: 1000 * 60, // 1 minute }) /** * Custom hook - encapsulates query logic * * Usage in component: * const { data, isPending, isError, error } = useTodos() */ export function useTodos() { return useQuery(todosQueryOptions) } /** * Fetch single todo by ID */ async function fetchTodoById(id: number): Promise { const response = await fetch( `https://jsonplaceholder.typicode.com/todos/${id}` ) if (!response.ok) { throw new Error(`Failed to fetch todo ${id}: ${response.statusText}`) } return response.json() } /** * Custom hook for fetching single todo * * Usage: * const { data: todo } = useTodo(1) */ export function useTodo(id: number) { return useQuery({ queryKey: ['todos', id], queryFn: () => fetchTodoById(id), enabled: !!id, // Only fetch if id is truthy }) } /** * Component usage example: */ export function TodoList() { const { data, isPending, isError, error, isFetching } = useTodos() if (isPending) { return
Loading todos...
} if (isError) { return
Error: {error.message}
} return (

Todos {isFetching && '(Refetching...)'}

    {data.map((todo) => (
  • {todo.title}
  • ))}
) } /** * Key states explained: * * - isPending: No data yet (initial fetch) * - isLoading: isPending && isFetching (loading for first time) * - isFetching: Any background fetch in progress * - isError: Query failed * - isSuccess: Query succeeded and data is available * - data: The fetched data (undefined while isPending) * - error: Error object if query failed */