Planteamiento y alcance
Mantienes un panel de varias columnas. El tamaño de una tarjeta está determinado por la cuadrícula, el arrastre, la barra lateral y la carga de fuentes, por lo que no puede tratarse únicamente como una función del tamaño del viewport. Por debajo de 320px muestra un título compacto y métricas en una sola columna; por encima de ese umbral muestra el diseño completo. Contraer la barra lateral sin cambiar el tamaño de la ventana debe surtir efecto de inmediato.
Esta pregunta evalúa la medición a nivel de elemento, los tiempos de layout del navegador, el ciclo de vida de React y los límites de rendimiento. El material público de entrevistas front-end comúnmente cubre DOM, CSS, eventos del navegador y rendimiento; la documentación de la plataforma ofrece detalles precisos sobre las notificaciones y el comportamiento de bucle de ResizeObserver para preguntas de seguimiento más profundas.
Qué está evaluando el entrevistador
- Explicar que
window.resizedescribe cambios en el viewport, no reorganizaciones de la cuadrícula, cambios de fuentes ni cambios en el tamaño del elemento padre. - Elegir
content-boxoborder-boxy definir qué significa el umbral. - Escribir mediciones en el estado o en variables CSS sin redimensionar sincrónicamente el elemento observado.
- Gestionar el reemplazo del objetivo, el desmontaje, nodos ocultos, navegadores antiguos y una gran cantidad de tarjetas.
- Demostrar fluidez con métricas de usuario, tareas largas (long tasks) y trazas de layout en lugar de basarse únicamente en el conteo de callbacks.
Preguntas para aclarar primero
- ¿El umbral se refiere al ancho del content-box, al ancho del border-box o es una decisión exclusivamente de estilo que las container queries de CSS pueden expresar?
- ¿Los cambios de tamaño deben desencadenar operaciones de datos o solo cambiar la presentación?
- ¿La interfaz de usuario debe actualizarse en cada cuadro (frame) o es suficiente una actualización fusionada (coalesced) por cuadro?
- ¿Cuál es el soporte mínimo de navegadores y puede el renderizado en servidor tocar
window? - ¿Hay decenas o miles de tarjetas, y se pueden observar solo las tarjetas visibles?
Una respuesta de 30 segundos
“Primero establecería que este es un problema del tamaño del elemento, no del tamaño del viewport. Para puntos de interrupción exclusivos de estilo, preferiría las container queries de CSS. Si JavaScript necesita la medición, asociaría un ResizeObserver en el cliente, leería una caja definida, deduplicaría el estado del umbral y escribiría una variable CSS o un estado discreto. El callback no debe cambiar sincrónicamente el tamaño observado; las escrituras no esenciales se pueden programar para el siguiente cuadro, y la limpieza debe desconectar el observador. Para muchas tarjetas limitaría el conjunto observado y mediría INP, tareas largas y el costo de layout. Los navegadores antiguos reciben un fallback explícito con diseño fijo, container queries o un listener de viewport con limitación de frecuencia (throttled).”
Respuesta detallada
Paso 1: Comprobar si CSS es suficiente
Si el requisito es únicamente “mostrar estilos compactos por debajo de 320px”, una container query suele ser más simple y mantiene la medición fuera de JavaScript. ResizeObserver se justifica cuando el tamaño condiciona el muestreo de gráficos, la virtualización, un renderizador de terceros o lógica de negocio observable.
Paso 2: Definir el contrato de tamaño
ResizeObserverEntry expone mediciones de content-box y border-box. Define qué caja rige el umbral, y luego estandariza las unidades y el redondeo. Una muestra con punto flotante no es automáticamente un evento de negocio; deduplica una transición de 319.9 a 320.1 como un cambio de estado de punto de interrupción cuando ese sea el contrato del producto.
Paso 3: Establecer el ciclo de vida del observador
Crea el observador después de que el componente del cliente se monte, observa el nodo de la tarjeta y llama a unobserve o disconnect cuando el nodo cambie o el componente se desmonte. En React, haz que una ref sea la propietaria del objetivo y que un Effect gestione la creación y limpieza del observador para que los renders ordinarios no lo recreen.
const ref = useRef<HTMLDivElement>(null)
const [compact, setCompact] = useState(false)
useEffect(() => {
const node = ref.current
if (!node || !('ResizeObserver' in window)) return
const observer = new ResizeObserver(([entry]) => {
const width = entry.contentRect.width
const next = width < 320
setCompact((current) => (current === next ? current : next))
})
observer.observe(node)
return () => observer.disconnect()
}, [])Paso 4: Prevenir un bucle de retroalimentación de callbacks
Si el callback cambia el ancho o la altura del elemento observado, ese cambio puede programar otra notificación y eventualmente producir ResizeObserver loop completed with undelivered notifications. Mantén el callback enfocado en el contrato de tamaño, o programa escrituras visuales con requestAnimationFrame y hazlas idempotentes.
Paso 5: Separar la medición del renderizado
Escribe las mediciones en una propiedad personalizada de CSS cuando CSS pueda gestionar el layout, o mantén únicamente un estado discreto como compact. No guardes cada cambio de píxel en el estado de React durante un arrastre. Si se requiere un valor continuo, agrupa las notificaciones por cuadro de animación y registra los cuadros perdidos y las tareas largas.
Paso 6: Manejar muchas tarjetas y nodos ocultos
Para miles de tarjetas, observa solo los nodos visibles o permite que una capa de layout distribuya las mediciones. display: none, los paneles contraídos y la virtualización cambian los tamaños observables; después de que un nodo se vuelve visible, verifica que la primera notificación restaure el estado correcto. Un observador no es un bucle de sondeo (polling).
Paso 7: Definir los límites de fallback y del servidor
El renderizado del lado del servidor no puede acceder a window. Detecta el soporte dentro de un Effect del cliente. Sin ResizeObserver, utiliza un diseño fijo, reglas de CSS media/container o un listener de viewport con throttle, y documenta el comportamiento a nivel de elemento que el fallback no puede proporcionar.
Paso 8: Verificar con evidencia
Prueba el colapso de la barra lateral, el arrastre, la carga de fuentes, el reflow de la cuadrícula, la rotación y el zoom del navegador. Registra callbacks, layout, pintura (paint) y tareas largas en Chrome Performance; usa INP o el retardo de entrada (input delay) para comprobar la respuesta al arrastrar. Agrega una prueba para la advertencia de bucle en la consola y verifica que una tarjeta desmontada ya no reciba actualizaciones.
Compensaciones y límites
Container queries frente a ResizeObserver
Las container queries se adaptan a puntos de interrupción exclusivos de estilo y se mantienen declarativas. ResizeObserver se adapta a cálculos de JavaScript y renderizadores de terceros, pero requiere controles de ciclo de vida y rendimiento. El límite radica en si la medición debe salir de la capa de estilos.
content-box frente a border-box
Usa content-box para umbrales de layout de contenido y border-box para un contrato de tarjeta exterior que incluye padding y bordes. Una elección incorrecta desplaza el punto de interrupción; incluye la elección en el contrato y en las pruebas.
Valores continuos frente a discretos
El ancho continuo puede alimentar un gráfico, pero se actualiza con frecuencia. Un punto de interrupción discreto es estable y más fácil de probar. Comienza con el contrato discreto y añade actualizaciones continuas solo con un presupuesto medido y un beneficio visual claro.
Simulacros de fallos y evolución
Fallo: escuchar únicamente window.resize
El colapso de la barra lateral y el reflow de la cuadrícula no cambian el viewport, por lo que la tarjeta se queda en el diseño incorrecto. Observa la tarjeta o su contenedor y prueba cambios que no dependan del viewport.
Fallo: mutar el tamaño observado dentro del callback
La mutación desencadena otro callback y puede crear un bucle y layout adicional. Escribe una variable CSS independiente, usa un estado discreto o programa una actualización idempotente para el siguiente cuadro.
Fallo: setState en cada píxel
El arrastre genera renders de React de alta frecuencia y empeora la respuesta a la interacción. Deduplica umbrales, fusiona con rAF, observa solo las tarjetas visibles y confirma el costo en la pestaña Performance.
Errores comunes y preguntas de seguimiento
Error: tratar a ResizeObserver como un evento resize más potente
Observa la caja de un elemento y entrega notificaciones de acuerdo con los tiempos de layout; no es un simple reemplazo de un evento del viewport.
Pregunta de seguimiento: ¿cómo se verifica React Strict Mode?
Asegúrate de que la configuración y la limpieza en desarrollo se mantengan emparejadas y que el recuento de observadores no se acumule. No confundas la verificación adicional de desarrollo con suscripciones duplicadas en producción.
Pregunta de seguimiento: ¿puede el callback llamar a getBoundingClientRect?
Puede hacerlo, pero la medición adicional puede agregar costo de layout. Prefiere los valores de la caja de la entrada (entry) y demuestra cualquier lectura adicional mediante una traza de rendimiento.
Pregunta de seguimiento: ¿cómo se prueba un bucle de tamaño?
Haz que el callback modifique un estilo que afecte a su propio tamaño, observa las advertencias, el recuento de notificaciones y el tamaño final, y luego valida que la versión corregida se estabilice sin notificaciones continuas.
Pregunta de seguimiento: ¿cuándo es innecesario JavaScript?
Cuando el requisito sea únicamente un cambio de estilo por punto de interrupción del contenedor, prefiere las container queries de CSS. Reserva JavaScript para datos, mediciones o renderizadores de terceros que realmente lo necesiten.
Pregunta de seguimiento: ¿cómo demuestras que la optimización funcionó?
Compara INP, el conteo de tareas largas, el tiempo de layout, los commits de React y la memoria bajo el mismo script de arrastre. El conteo de callbacks por sí solo no es una métrica de experiencia de usuario.