En esta clase vamos a integrar correctamente los filtros dentro del flujo de búsqueda de ofertas de trabajo, haciendo que la petición a la API se actualice cada vez que el usuario cambia un filtro (tecnología, texto, tipo, experiencia, etc).
El problema inicial
Aunque la aplicación ya hace una llamada HTTP a la API, hay un problema: no está usando los filtros seleccionados por el usuario.
Los filtros se actualizan en el estado correctamente, pero esa información no se envía en la petición a la API.
Objetivo
Cuando alguien escribe “JavaScript” o cambia cualquier filtro, la app debe volver a consultar la API, enviando esos filtros como parámetros.
Estado de los filtros
El estado de los filtros ya existe y funciona correctamente:
- Se pasa al formulario de búsqueda
- Se actualiza cuando el usuario interactúa con los filtros
- No hace falta tocar esa parte
El único problema es que la petición a la API no lee esos filtros antes de ejecutarse.
Cómo enviar filtros a la API
La API acepta filtros en la URL mediante query params, por ejemplo:
?text=react&limit=10&offset=0
La API responde correctamente a parámetros como:
text- Búsqueda por textotechnology- Filtro por tecnología (javascript, python, etc.)type- Filtro por tipo/ubicación de trabajo (remote, onsite, hybrid)level- Filtro por nivel de experiencia (junior, mid, senior)
El siguiente paso es construir esos parámetros dinámicamente.
Construcción de los query params
Para construir los parámetros de la URL, utilizamos URLSearchParams, que forma parte del estándar de la plataforma web.
Implementación paso a paso
const params = new URLSearchParams()
// Si existe texto de búsqueda
if (textToFilter) {
params.append('text', textToFilter)
}
// Si existe filtro de tecnología
if (filters.technology) {
params.append('technology', filters.technology)
}
// Si existe filtro de ubicación/tipo
if (filters.location) {
params.append('type', filters.location)
}
// Si existe filtro de nivel de experiencia
if (filters.experienceLevel) {
params.append('level', filters.experienceLevel)
}
// Convertir a string y concatenar a la URL
const queryParams = params.toString()
const url = `https://jscamp-api.vercel.app/api/jobs?${queryParams}`
Ventaja de este enfoque
Si no hay filtros activos, searchParams.toString() devuelve una cadena vacía, y la URL queda simplemente con un ? al final, lo cual funciona perfectamente.
Primer intento y error detectado
Al hacer una prueba en la página:
- Refrescas
- Seleccionas tecnología → JavaScript
- No funciona ❌
¿Por qué no funciona?
El useEffect que hace la petición solo se ejecuta una vez, porque su array de dependencias está vacío:
useEffect(() => {
// Petición a la API
}, []) // ❌ Array vacío = solo se ejecuta al montar el componente
También aparece una advertencia del linter:
El hook debería listar sus dependencias o eliminar el array (pero si lo quitas, generas un loop infinito porque se re-renderiza sin parar).
La solución correcta
Las dependencias que deben hacer que el efecto se ejecute son:
textToFilter- El texto de búsquedafilters.technology- Filtro de tecnologíafilters.location- Filtro de ubicación/tipofilters.experienceLevel- Filtro de nivel de experiencia
Añadiendo las dependencias
useEffect(() => {
const params = new URLSearchParams()
if (textToFilter) {
params.append('text', textToFilter)
}
if (filters.technology) {
params.append('technology', filters.technology)
}
if (filters.location) {
params.append('type', filters.location)
}
if (filters.experienceLevel) {
params.append('level', filters.experienceLevel)
}
const queryParams = params.toString()
fetch(`https://jscamp-api.vercel.app/api/jobs?${queryParams}`)
.then((res) => res.json())
.then((data) => setJobs(data))
}, [textToFilter, filters.technology, filters.location, filters.experienceLevel]) // ✅ Dependencias correctas
Ahora el efecto se ejecutará cada vez que cambie alguno de estos valores.
Comprobación
Haciendo pruebas en directo:
Búsqueda por texto
- Buscas “Python” → ✅ Funciona
- Buscas “JavaScript” → ✅ Funciona
Combinación de filtros
JavaScript + remoto → ✅ La API recibe ambos filtros y devuelve resultados correctamente
En la interfaz aparecen los filtros activos:
- Technology: JavaScript
- Type: Remote
Caso sin resultados
Para mejorar la experiencia de usuario, añadimos un mensaje cuando no hay resultados:
{
jobs.length === 0 ? (
<p
style={{
padding: '2rem',
textWrap: 'balance',
}}
>
No se han encontrado empleos que coincidan con los criterios de búsqueda.
</p>
) : (
<JobsList jobs={jobs} />
)
}
Mejoras aplicadas
- Padding para mejor presentación
text-wrap: balancepara que el texto se distribuya mejor
Todo funcionando
Ejemplo final con múltiples filtros:
- Texto: “JavaScript”
- Tipo: Remoto
- Nivel: Junior
La búsqueda devuelve el resultado correcto, filtrando por todos los criterios seleccionados.
Resumen final
En esta clase has aprendido a:
- ✅ Identificar por qué los filtros no funcionaban inicialmente
- ✅ Usar
URLSearchParamspara construir query params dinámicamente - ✅ Añadir dependencias correctas al
useEffect - ✅ Integrar los filtros en la petición HTTP
- ✅ Manejar el caso de búsquedas sin resultados
- ✅ Probar que todo funciona con múltiples combinaciones de filtros
Ahora el sistema de filtros está completamente integrado y funcional. Cada vez que el usuario cambia un filtro, la aplicación hace una nueva petición a la API con los parámetros actualizados.
Pero todavía no tenemos paginación. ¡Vamos a ello!