En esta clase te propongo varios ejercicios para mejorar la aplicación de empleos que hemos estado construyendo. Son mejoras que te permitirán practicar conceptos importantes y hacer la aplicación más profesional.

Introducción a las mejoras posibles

Aunque la aplicación ya funciona correctamente, todavía hay espacio para añadir más funcionalidades que mejoren la experiencia de usuario y te permitan practicar conceptos avanzados de React.

Estos ejercicios están diseñados para que los resuelvas por tu cuenta, aplicando todo lo que has aprendido hasta ahora.

Ejercicio 1: Botón de “Limpiar filtros”

Descripción

Añade un botón que permita limpiar todos los filtros aplicados de una sola vez.

Requisitos

1. El botón solo debe aparecer cuando hay filtros activos

El botón no debe mostrarse si no hay ningún filtro aplicado. Esto significa que debes:

  • Crear una función o helper que determine si hay filtros activos
  • Usar renderizado condicional para mostrar/ocultar el botón

Pista: Necesitas crear una función que verifique si alguna propiedad del objeto filters no está vacía. Piensa en usar operadores lógicos (||) o métodos como Object.values().

2. Implementar handleClearFilters

Debes crear una función que:

  • Limpie todos los filtros
  • Resetee el estado a los valores iniciales
  • Opcionalmente, también podría resetear la paginación

Pista: Esta función debe llamar a setFilters con un objeto que tenga todas las propiedades vacías. Considera si también quieres resetear otros estados como la página actual.

3. Mostrar el botón solo cuando corresponda

El botón solo debe aparecer en el DOM cuando realmente haya filtros activos.

Pista: Usa el operador && para renderizado condicional: {condicion && <Componente />}

Conceptos implicados

  • Renderizado condicional
  • Gestión del estado
  • Mejora de UX
  • Funciones helper

Pistas

  • Piensa en qué significa “filtros activos”
  • El botón debería resetear todo al estado inicial
  • Considera si también quieres resetear la página actual

Ejercicio 2: Mostrar un spinner de carga

Descripción

Mejora la experiencia de carga sustituyendo el texto simple por un spinner visual más atractivo.

Requisitos

1. Crear un componente Spinner

Pista: Crea un componente que devuelva un div con una clase CSS. Para la animación:

  • Usa border-radius: 50% para hacer un círculo
  • Usa border con diferentes colores (uno diferente para el borde superior)
  • Crea una animación @keyframes que rote de 0 a 360 grados
  • Aplica la animación con animation: nombre-animacion duración linear infinite

2. Reemplazar el texto de carga

Pista: En lugar de mostrar solo texto cuando loading es true, devuelve un contenedor que incluya:

  • Tu componente <Spinner />
  • Un mensaje de texto descriptivo
  • Estilos CSS para centrarlo en la página

Conceptos implicados

  • Componentes reutilizables
  • Animaciones CSS
  • Mejora de UX
  • Feedback visual

Pistas

  • Puedes encontrar muchos ejemplos de spinners CSS en internet
  • También puedes usar una librería como react-spinners
  • Asegúrate de que el spinner esté centrado y sea visible

Ejercicio 3: Guardar filtros en LocalStorage

Descripción

Persiste los filtros del usuario usando localStorage, de forma que cuando recargue la página, los filtros se mantengan.

¿Qué es LocalStorage?

localStorage es una API del navegador que permite guardar datos de forma persistente. Los métodos principales son:

  • localStorage.setItem(clave, valor) - Guardar datos
  • localStorage.getItem(clave) - Leer datos
  • localStorage.removeItem(clave) - Eliminar datos
  • localStorage.clear() - Limpiar todo

Importante: localStorage solo acepta strings, así que necesitas:

  • Usar JSON.stringify(objeto) antes de guardar
  • Usar JSON.parse(string) después de leer

Requisitos

1. Guardar filtros cada vez que cambien

Pista: Usa un useEffect con filters como dependencia. Dentro del efecto:

  • Convierte el objeto filters a string con JSON.stringify()
  • Guárdalo en localStorage con una clave descriptiva (ej: ‘jobFilters’)

2. Leer filtros al cargar la app

Pista: Usa la inicialización “lazy” de useState pasándole una función:

  • useState(() => { ... }) - La función solo se ejecuta una vez al montar
  • Dentro, intenta leer de localStorage con getItem()
  • Si existe, usa JSON.parse() para convertirlo de string a objeto
  • Si no existe, devuelve el objeto inicial con valores vacíos

3. Limpiar el localStorage al limpiar filtros

Pista: En tu función handleClearFilters, además de resetear el estado, usa localStorage.removeItem() con la misma clave que usaste para guardar.

Conceptos implicados

  • Persistencia de datos
  • API del navegador
  • useEffect con dependencias
  • Lazy initialization del estado
  • Serialización/deserialización de datos

Pistas adicionales

  • Usa la forma de función en useState para la inicialización
  • Recuerda que localStorage.getItem() devuelve null si no existe la clave
  • Importante: Envuelve todo en try-catch - JSON.parse() puede fallar si los datos están corruptos
  • Considera guardar también la página actual si quieres una experiencia más completa
  • Puedes crear un custom hook usePersistedFilters para encapsular toda esta lógica

Ejercicio 4: Manejo de errores

Descripción

Actualmente la aplicación no gestiona adecuadamente los errores que pueden ocurrir al hacer peticiones a la API. Es momento de mejorar esto.

Requisitos

1. Añadir estado para errores

Pista: Crea un nuevo estado con useState inicializado en null para almacenar los mensajes de error.

2. Capturar errores en el fetch

Pista: En tu useEffect donde haces el fetch:

  • Envuelve todo en un bloque try-catch-finally
  • En el try:
    • Resetea el error a null al inicio
    • Verifica response.ok - si es false, lanza un error con throw new Error()
    • Procesa la respuesta normalmente
  • En el catch: guarda el mensaje de error en el estado
  • En el finally: actualiza el estado de loading a false (se ejecuta siempre)

3. Mostrar mensajes de error al usuario

Pista: Usa renderizado condicional para mostrar un mensaje cuando error no sea null:

  • Muestra un título descriptivo
  • Muestra el mensaje de error del estado
  • Incluye un botón para reintentar (puede recargar la página con window.location.reload() o resetear el estado)

4. Diferentes tipos de errores (Bonus)

Pista: Para una mejor experiencia, crea una función helper que devuelva mensajes personalizados según el tipo de error:

  • Usa navigator.onLine para detectar si hay conexión a internet
  • Verifica el código de estado HTTP (404, 500, etc.) en el mensaje de error
  • Devuelve mensajes claros y accionables para el usuario
  • Ten un mensaje genérico como fallback

Conceptos implicados

  • Manejo de errores asíncronos
  • Try-catch
  • Estados de error
  • Feedback al usuario
  • UX en casos de error

Pistas

  • Siempre verifica response.ok antes de procesar la respuesta
  • Proporciona mensajes de error claros y útiles
  • Ofrece una forma de recuperarse del error (botón de reintentar)
  • Considera usar ErrorBoundary para errores de React

Resumen de los ejercicios

EjercicioDificultadConceptos principales
Botón limpiar filtros⭐⭐Renderizado condicional, gestión del estado
Spinner de cargaComponentes, CSS, UX
Persistencia con localStorage⭐⭐⭐localStorage, useEffect, serialización
Manejo de errores⭐⭐⭐Try-catch, estados de error, UX

Bonus: Combinación de mejoras

Una vez completados todos los ejercicios, tendrás una aplicación mucho más robusta:

Filtros persistentes - El usuario no pierde sus filtros al recargar

Mejor feedback visual - Spinners en lugar de texto simple

Fácil limpieza de filtros - Un solo clic para resetear todo

Manejo robusto de errores - La app no se rompe ante problemas de red

Consejos finales

Para el botón de limpiar filtros

  • Piensa en dónde colocarlo visualmente (junto a los filtros, en la barra de búsqueda, etc.)
  • Considera añadir un contador de filtros activos (“3 filtros aplicados”)

Para el spinner

  • No abuses de las animaciones (pueden distraer)
  • Asegúrate de que sea accesible (añade aria-label)
  • Considera diferentes estados (cargando, éxito, error)

Para localStorage

  • Ten cuidado con el tamaño de los datos (localStorage tiene límites)
  • Siempre maneja el caso de que el usuario tenga localStorage deshabilitado
  • Considera usar un prefijo para las claves (jobApp_filters en lugar de solo filters)

Para el manejo de errores

  • Sé específico en los mensajes de error
  • Proporciona acciones claras al usuario
  • No muestres errores técnicos al usuario final
  • Registra los errores en la consola para debugging

Conclusión

Estos ejercicios te permitirán practicar conceptos avanzados de React y mejorar significativamente la experiencia de usuario de tu aplicación.

No te preocupes si no consigues resolverlos todos a la primera. Estos son ejercicios de nivel intermedio-avanzado que requieren investigación y prueba-error.

Lo importante es:

  1. Intentarlo por tu cuenta antes de buscar soluciones
  2. Investigar cuando te quedes atascado
  3. Entender por qué funciona la solución
  4. Experimentar con variaciones

💡 Tip: Implementa cada mejora por separado y asegúrate de que funciona antes de pasar a la siguiente. Es mejor hacer las cosas paso a paso que intentar hacerlo todo a la vez.

¡Manos a la obra! 🚀