TanStack Query: Hook personalizado para React

Clase 27 de 31Curso de React Avanzado

Resumen

TanStack Query, antes React Query, simplifica el consumo de datos en React con una API clara y caché integrada. Aquí verás cómo crear un custom hook con useQuery para leer un JSON local, manejar errores y aprovechar una key de caché. El objetivo: una app de cursos paginada que muestra dos cursos por página, lista para crecer y mantenerse limpia.

¿Qué es TanStack Query y qué problema resuelve?

TanStack Query ya no es exclusivo de React: también funciona con Vanilla JavaScript, TypeScript, View y Svelte. La idea central es facilitar el consumo de APIs y el manejo del estado asíncrono sin reimplementar lógica repetitiva.

  • Soporta múltiples librerías y frameworks.
  • Integra caché, revalidación y estados de carga.
  • Evita duplicar lógica con custom hooks.
  • Se complementa con fetch API, Axios o hooks propios.

¿Por qué usar la caché con key en useQuery?

La key es un identificador único. Con ella, TanStack Query almacena resultados en caché y evita un refetch innecesario. Así, mejoras rendimiento y reduces llamadas de red al navegar por la interfaz.

¿Qué papel cumple la query function?

La query function es una promesa que hace el fetch al recurso (en este caso, un JSON local). Si resuelve, entrega los datos listos para usarse; si falla, puedes manejar el error de forma controlada.

¿Cómo preparar el proyecto y los datos JSON?

Se crea el proyecto desde cero con React y TypeScript, se instala la librería y se arranca el entorno con NPM. La app de ejemplo “Learning Courses” muestra cursos paginados de a dos por página usando un archivo JSON interno.

  • Crear estructura y ejecutar NPM install.
  • Iniciar con NPM run dev.
  • Instalar TanStack Query desde su documentación oficial.
  • Guardar el archivo en Public/API/courses.json.

El JSON de cursos incluye por objeto: id numérico, título, descripción y duración. Puedes extenderlo con imágenes u otros campos según necesidad.

¿Cómo organizar carpetas y archivos?

La organización favorece claridad y escalabilidad.

  • Carpeta components para vistas reutilizables.
  • Carpeta hooks para lógica de datos.
  • Archivo CourseList.tsx en components.
  • Archivo useCourses en hooks para el fetch.
  • Eliminar archivos de plantilla que no se usen.

¿Cómo implementar el fetch con useQuery y un custom hook?

El flujo es simple: defines la interfaz del curso, creas la función de fetch tipada y expones un custom hook que utiliza useQuery con su key y su query function.

// hooks/useCourses.ts
import { useQuery, QueryFunction } from '@tanstack/react-query'

// 1) Interfaz de dominio
export interface Course {
  id: number
  title: string
  description: string
  duration: string
}

// 2) Query function: fetch al JSON local
const fetchCourses: QueryFunction<Course[]> = async () => {
  const response = await fetch('API/courses.json')
  if (!response.ok) {
    throw new Error('Network response was not ok')
  }
  return response.json()
}

// 3) Custom hook con useQuery y key de caché
export const useCourses = () => {
  return useQuery<Course[]>({
    queryKey: ['cursos'],
    queryFn: fetchCourses,
  })
}

Con este hook, cualquier componente puede leer cursos, mostrar estados de carga y reaccionar a errores sin duplicar lógica.

// components/CourseList.tsx
import { useCourses } from '../hooks/useCourses'

export function CourseList() {
  const { data, isLoading, error } = useCourses()

  if (isLoading) return <p>Cargando...</p>
  if (error) return <p>Ocurrió un error al cargar los cursos.</p>

  return (
    <ul>
      {data?.map((course) => (
        <li key={course.id}>
          <h3>{course.title}</h3>
          <p>{course.description}</p>
          <small>Duración: {course.duration}</small>
        </li>
      ))}
    </ul>
  )
}

Puntos clave del enfoque:

  • Peticiones asíncronas con async/await y manejo de errores.
  • Caché por key para evitar refetch innecesario.
  • Tipado estricto con TypeScript para datos fiables.
  • Reutilización mediante custom hooks.
  • Datos locales desde Public/API/courses.json; lista paginada de a dos cursos.

¿Te gustaría ver cómo añadir el paginado y filtros con la misma estructura? Comenta qué parte quieres profundizar y tu caso de uso.