DevPath · Aprende a programar ESPTEN

Datos y estado global

You Might Not Need an Effect

El abuso de useEffect

useEffect es una de las herramientas más usadas en exceso de React. Mucha gente lo trata como "código que corre cuando cambia algo", y lo mete por todas partes. Pero un efecto sirve para una cosa concreta: sincronizar tu componente con un sistema externo (la red, el DOM directo, un setInterval, una suscripción...). Si lo usas para otra cosa, casi siempre hay una opción mejor, más simple y sin bugs.

Antipatrón: estado "derivado" en estado + efecto

Caso clásico: tienes nombre y apellido en estado y quieres el nombre completo. La tentación es un tercer estado sincronizado con un efecto:

// ❌ Innecesario: estado redundante + efecto
function Formulario() {
  const [nombre, setNombre] = useState("Ada");
  const [apellido, setApellido] = useState("Lovelace");
  const [completo, setCompleto] = useState("");

  useEffect(() => {
    setCompleto(nombre + " " + apellido);
  }, [nombre, apellido]);
  // ...
}

Esto es peor en todos los sentidos: hay un estado de más, un render extra (el efecto corre después de pintar, y setCompleto provoca otro render) y un momento en que completo está desincronizado.

La regla: si puedes calcularlo a partir de props/estado, hazlo en el render

El nombre completo es estado derivado: no es información nueva, se deriva de lo que ya tienes. Calcúlalo durante el render, como una variable normal:

// ✅ Derivado durante el render
function Formulario() {
  const [nombre, setNombre] = useState("Ada");
  const [apellido, setApellido] = useState("Lovelace");
  const completo = nombre + " " + apellido; // se recalcula en cada render
  // ...
}

Sin efecto, sin estado extra, sin desincronización. Esto vale para filtrar una lista, contar elementos, formatear un valor, saber si algo está vacío... todo eso se calcula en el render. (Si el cálculo es realmente caro, envuélvelo en useMemo para no repetirlo en cada render; pero useMemo es solo una optimización, no un cambio de enfoque.)

Antipatrón: lógica de evento dentro de un efecto

Otro mal uso: ejecutar lógica que debería ir en un manejador de eventos.

// ❌ Reaccionar a un cambio de estado con un efecto para "hacer algo"
useEffect(() => {
  if (enviado) {
    mostrarToast("¡Compra realizada!");
    vaciarCarrito();
  }
}, [enviado]);

Esa lógica ocurre porque el usuario hizo clic en "Comprar", no porque el componente se sincronice con un sistema externo. Su sitio es el handler:

// ✅ La lógica de la interacción va en el manejador del evento
function comprar() {
  enviarPedido();
  mostrarToast("¡Compra realizada!");
  vaciarCarrito();
}

Regla práctica: si algo pasa porque el usuario hizo algo (un clic, un submit), va en el manejador de eventos. Si algo pasa porque el componente apareció en pantalla y debe sincronizarse con algo externo, va en un efecto.

¿Cuándo SÍ necesitas un efecto?

Un efecto es la herramienta correcta para sincronizar con sistemas externos a React:

// ✅ Sincronizar con un sistema externo: con limpieza
useEffect(() => {
  const id = setInterval(() => setSegundos((s) => s + 1), 1000);
  return () => clearInterval(id); // limpieza al desmontar
}, []);

Antes de escribir un useEffect, pregúntate: ¿estoy sincronizando con algo externo, o solo intento calcular un valor o responder a una interacción? En los dos últimos casos, You Might Not Need an Effect.

Pon esto en práctica

DevPath es un curso práctico: aquí lees la teoría; en la app la pones en práctica con ejercicios que se ejecutan de verdad, sin conexión.

Empezar gratis en la app →
← Estado global: más allá de ContextVer el módulo →