DEV Community

Silviu Technology
Silviu Technology

Posted on

React: reenvíos de verificación sin ansiedad

En muchos productos el botón de "reenviar verificación" parece un detalle menor, pero suele decidir si el usuario termina el alta o abandona. Cuando la interfaz no explica si el correo ya salió, cuánto falta para volver a intentarlo o por qué el botón sigue desactivado, la sensación es rara. No parece rota del todo, pero tampoco transmite confianza.

En frontend me encuentro este problema bastante seguido. El backend responde bien, la cola manda el email, pero la UI deja al usuario adivinando. Algunos equipos incluso prueban con cuentas de tem email o con inboxes temporales para ver si "más o menos" funciona, y justo ahí aparecen los bordes raros: loaders que parpadean, temporizadores que cambian de ancho y mensajes que desaparecen demasiado pronto.

Por qué el botón de reenviar crea desconfianza

El usuario no piensa en estados finitos ni en throttling. Piensa algo mucho más simple: "¿ya envié el correo o lo vuelvo a tocar?". Si la interfaz no responde eso rápido, empieza el martilleo del botón, llegan solicitudes duplicadas y soporte recibe tickets que eran evitables.

También hay un tema claro de accesibilidad. Un temporizador visual sin anuncio comprensible deja fuera a usuarios con lector de pantalla, y un mensaje efímero puede perderse si el foco sigue en otro sitio. La guía de WAI sobre mensajes de estado explica por qué conviene anunciar cambios relevantes sin romper la navegación: https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html.

Desde rendimiento percibido, el problema tampoco es pequeño. Nielsen Norman Group lleva años señalando que el feedback visible dentro de los primeros segundos reduce ansiedad y mejora la sensación de control: https://www.nngroup.com/articles/response-times-3-important-limits/. No hace milagros, pero si baja bastante la fricción cuando el correo tarda un poco más de lo esperado.

Qué estados necesita un reenvío usable

Para mí el patrón mínimo tiene cuatro estados: idle, sending, cooldown y sent-with-help. El primero permite enviar. El segundo confirma que ya se disparó la acción. El tercero evita reintentos impulsivos y explica cuánto falta. El cuarto da una salida si el correo no aparece: revisar spam, corregir dirección o usar otra bandeja de prueba.

Lo importante es que esos estados no cambien la estructura principal. El mismo botón debe seguir ocupando el mismo espacio. El mensaje de ayuda necesita una altura estable. Y el contador no debería empujar el layout cada segundo, por que ese tipo de micro salto se siente mas de lo que parece.

Aquí también entra la parte pragmática: si tu equipo usa una bandeja temporal para QA, conviene separar el flujo de usuario real del flujo de prueba. A veces basta con documentar cuándo usar una cuenta normal y cuándo una herramienta como temp mail so. No necesita ser protagonista del artículo ni del producto, solo un recurso contextual cuando el equipo valida entregas o tiempos de llegada.

Un patrón simple en React

Este ejemplo mantiene el ancho del CTA, anuncia cambios útiles y evita que el contador rehaga media interfaz a cada tick. No es el único enfoque, pero sale bastante bien en productos con onboarding:

import { useEffect, useRef, useState } from "react";

const COOLDOWN_SECONDS = 30;

export function ResendVerification() {
  const [status, setStatus] = useState("idle");
  const [secondsLeft, setSecondsLeft] = useState(0);
  const [message, setMessage] = useState("");
  const timerRef = useRef(null);

  useEffect(() => {
    if (status !== "cooldown") return;

    timerRef.current = window.setInterval(() => {
      setSecondsLeft((current) => {
        if (current <= 1) {
          window.clearInterval(timerRef.current);
          setStatus("idle");
          setMessage("Ya puedes reenviar el correo otra vez.");
          return 0;
        }

        return current - 1;
      });
    }, 1000);

    return () => window.clearInterval(timerRef.current);
  }, [status]);

  async function handleResend() {
    setStatus("sending");
    setMessage("Enviando otro correo de verificación...");

    const response = await fetch("/api/email/resend", { method: "POST" });

    if (!response.ok) {
      setStatus("idle");
      setMessage("No pudimos reenviar el correo. Revisa la dirección e inténtalo de nuevo.");
      return;
    }

    setStatus("cooldown");
    setSecondsLeft(COOLDOWN_SECONDS);
    setMessage("Correo reenviado. Espera un momento antes de repetir la acción.");
  }

  return (
    <section aria-live="polite">
      <p style={{ minHeight: 24 }}>{message}</p>

      <button
        type="button"
        onClick={handleResend}
        disabled={status === "sending" || status === "cooldown"}
        style={{ minWidth: 220 }}
      >
        {status === "cooldown"
          ? `Reenviar en ${secondsLeft}s`
          : status === "sending"
            ? "Enviando..."
            : "Reenviar verificación"}
      </button>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

Hay tres cosas pequeñas que me gustan aquí. Una: el texto de estado vive en un contenedor estable. Dos: el botón mantiene ancho mínimo, así que el cambio entre "Enviando..." y el contador no provoca brincos feos. Tres: el usuario entiende qué pasa sin tener que deducirlo, que suena básico, pero no siempre pasa.

Si quieres ir un poco más lejos, separa el countdown visual del anuncio accesible. Por ejemplo, puedes mantener aria-live solo para eventos grandes y no para cada segundo del contador. Eso evita ruido en lector de pantalla, que a veces se vuelve medio insoportable.

Cómo medir si la experiencia mejora

No me quedaría solo con "se siente mejor". Hay métricas concretas que ayudan:

  • ratio de clicks repetidos sobre reenviar en menos de 10 segundos
  • tiempo hasta completar verificación después del primer resend
  • tickets de soporte ligados a "no me llegó el correo"
  • abandono del onboarding tras ver el estado de cooldown

Si el ratio de clicks repetidos baja, normalmente la UI está explicando mejor lo que ya hacía el sistema. Y si además reduces abandono, mejor todavía. Para equipos que conectan estas pruebas con operaciones o automatización, me parecen utiles estos ejemplos sobre checks de email en ventanas de mantenimiento reales y runbooks de email que si escalan. Muestran bien cómo una interacción pequeña puede terminar afectando observabilidad y soporte.

Preguntas rápidas

¿El cooldown no frustra más al usuario?

Solo si aparece sin contexto. Si explicas que el correo ya salió y muestras cuánto falta, suele bajar la ansiedad en vez de subirla. Lo confuso no es esperar; lo confuso es esperar sin saber por qué.

¿Debo ocultar el botón por completo durante el cooldown?

Yo no lo haría. Prefiero dejar el mismo botón desactivado con texto útil. Cuando desaparece del DOM, el flujo se siente inconsistente y aveces rompe foco o layout.

¿Qué pasa si el correo tarda mucho?

Añade una vía de escape después de cierto tiempo: revisar spam, editar dirección o pedir ayuda. El peor caso no es la latencia; es la sensación de que la interfaz te dejó solo.

Un buen flujo de reenvío en React no necesita más animación ni más complejidad. Necesita estados honestos, espacio estable y mensajes que bajen la duda en vez de amplificarla. Cuando eso está cuidado, la experiencia se nota más rapida, más clara y bastante más humana.

Top comments (0)