Tema representativo de entrevista

Entrevista frontend: ¿Cómo diseñar una tarjeta de panel que responda al tamaño de su contenedor?

FrontendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Una tarjeta de panel se puede redimensionar mediante interacciones de arrastre y puede cambiar de ancho cuando se contrae una barra lateral, se cargan fuentes o la cuadrícula (grid) se reorganiza (reflow). Debe cambiar a un diseño compacto por debajo de los 320px mientras se mantiene fluida durante un redimensionamiento continuo. Diseña la implementación, explica por qué window.resize no es suficiente, muestra cómo evitar bucles de retroalimentación en ResizeObserver y describe la verificación de rendimiento y compatibilidad.

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.resize describe cambios en el viewport, no reorganizaciones de la cuadrícula, cambios de fuentes ni cambios en el tamaño del elemento padre.
  • Elegir content-box o border-box y 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.

tsx
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.

Fuentes públicas

Preguntas relacionadas