DEV Community

Silviu Technology
Silviu Technology

Posted on

React: validación async sin saltos visuales

Un formulario que valida un email en segundo plano parece sencillo. El usuario escribe, pulsamos un botón y esperamos una respuesta. Pero en una interfaz real aparecen detalles menos bonitos: el botón cambia de ancho, el mensaje de error empuja los campos, el foco se pierde y una persona que usa lector de pantalla no sabe si la petición sigue viva.

En este artículo propongo un patrón pequeño para React: modelar explícitamente los estados, reservar el espacio del feedback y tratar la accesibilidad como parte del diseño. Es útil para un signup, una verificación de dominio o un flujo que comprueba una dirección antes de enviar datos. La palabra clave no es hacer la petición más rapido, sino hacer visible lo que está pasando.

El problema: validar cambia la forma de la pantalla

El error típico es renderizar el mensaje solo cuando llega la respuesta:

{error && <p className="error">{error}</p>}
Enter fullscreen mode Exit fullscreen mode

Ese p no ocupa espacio durante el primer render. Cuando aparece, todo lo que está debajo baja unos píxeles. En móvil puede ser bastante molesto; en una pantalla con varios campos, el usuario pierde el lugar donde estaba leyendo.

También suele haber un segundo problema: usar solo el color rojo para indicar un email no válido. El color ayuda, pero no puede cargar con toda la explicación. Y si el texto alternativo se inserta sin anunciarse, la respuesta llega técnicamente, pero no llega a la persona.

Un modelo pequeño de estados

Para empezar, separo cuatro estados: idle, checking, valid y invalid. No hace falta una librería de state machine para esto.

const [email, setEmail] = useState('');
const [status, setStatus] = useState('idle');
const [message, setMessage] = useState('');

async function checkEmail() {
  setStatus('checking');
  setMessage('Comprobando la dirección…');

  const result = await validateEmail(email);
  setStatus(result.ok ? 'valid' : 'invalid');
  setMessage(result.ok
    ? 'La dirección parece válida.'
    : 'Revisa el formato de la dirección.');
}
Enter fullscreen mode Exit fullscreen mode

El estado checking importa. Sin él, el usuario puede pulsar varias veces y no queda claro cual respuesta pertenece a cual intento. En producción conviene además desactivar la acción mientras se verifica o usar un identificador de petición para ignorar respuestas antiguas.

Reservar espacio antes de hacer la petición

El feedback debe tener una caja estable desde el principio. min-height es suficiente para un mensaje corto:

.field-feedback {
  min-height: 1.5rem;
  margin-top: 0.35rem;
  font-size: 0.9rem;
}

.field-feedback[data-status="invalid"] {
  color: #a51d2d;
}

.field-feedback[data-status="valid"] {
  color: #176b3a;
}
Enter fullscreen mode Exit fullscreen mode

Luego dejamos que la caja exista aunque esté vacía:

<p
  className="field-feedback"
  data-status={status}
  aria-live="polite"
>
  {message}
</p>
Enter fullscreen mode Exit fullscreen mode

Así reducimos cambios de layout y mantenemos la jerarquía visual. No es una promesa de que el rendimiento será perfecto, pero elimina un movimiento innecesario y hace el flujo más tranquilo.

Accesibilidad: foco, mensajes y lectores de pantalla

Relaciona el input con su mensaje mediante aria-describedby, y marca el error sin depender de estilos:

<label htmlFor="email">Correo electrónico</label>
<input
  id="email"
  value={email}
  onChange={(event) => setEmail(event.target.value)}
  aria-invalid={status === 'invalid'}
  aria-describedby="email-feedback"
/>
<p id="email-feedback" className="field-feedback" aria-live="polite">
  {message}
</p>
Enter fullscreen mode Exit fullscreen mode

aria-live="polite" permite anunciar el resultado sin interrumpir cada tecla. El foco normalmente debe quedarse en el input: moverlo automáticamente al mensaje suele sentirse agresivo, especialmente si la validación ocurre mientras se escribe. Si el envío falla, entonces sí puede ser razonable enfocar el primer campo con error.

Para validar una dirección usada en pruebas, quizá alguien busque una fake email address. El producto debe explicar igualmente qué datos acepta y qué usos no permite; una etiqueta confusa como “tempail” no ayuda a nadie, y “fake e mail com” tampoco es un mensaje de interfaz.

Este enfoque complementa bien otros consejos sobre feedback de email sin romper el foco y sobre contratos de correo que sí se pueden probar.

Rendimiento y cancelación

No valides contra el servidor en cada pulsación sin una razón clara. Un debounce de 300–500 ms reduce trabajo, pero no debe esconder el estado. Para peticiones largas, AbortController evita que una respuesta vieja pinte sobre una nueva.

También revisa el tamaño del spinner. Un icono que aparece y desaparece puede cambiar el ancho del botón. Reserva su sitio o usa un botón con ancho estable. Son detalles pequeños, pero la percepción de calidad se construye con muchos detalles pequeños.

Checklist para revisar el formulario

  • ¿El usuario sabe cuándo la validación está en curso?
  • ¿El mensaje tiene espacio reservado y no mueve el contenido?
  • ¿El error se expresa con texto, no solo con color?
  • ¿aria-invalid y aria-describedby reflejan el estado actual?
  • ¿El foco permanece donde la persona lo dejó?
  • ¿Se ignoran respuestas antiguas o se cancelan peticiones?
  • ¿El flujo sigue siendo entendible con teclado y lector de pantalla?

Un formulario accesible no necesita parecer una consola de operaciones. Con estados explícitos, una caja de feedback estable y mensajes honestos, React puede ofrecer una experiencia más clara, medible y facil de mantener.

Top comments (0)