CodeForge

React 19 de Cero a Experto / Efectos y ciclo de vida

Cuándo NO usar useEffect (la lección clave)

Teoría24 min20 XP

Aquí viene el consejo que separa a quien "usa React" de quien lo ENTIENDE: la mayoría de los useEffect que la gente escribe NO deberían existir. useEffect es para sincronizar con sistemas EXTERNOS (el DOM, una API, un timer). Si lo estás usando para transformar datos, para recalcular algo a partir del estado, o para responder a un clic, casi seguro hay una forma más simple, más rápida y con menos bugs. Hoy aprendes a NO usar efectos —quizá la habilidad más valiosa del módulo—.

El efecto que sobra

Error nº1: sincronizar estado derivado

El antipatrón más común: tener un estado y un efecto que lo mantiene "en sync" con otro estado.

derivado-mal.tsx
// ❌ MAL: 'total' es un estado sincronizado por un efecto
const [gastos, setGastos] = useState<Gasto[]>([]);
const [total, setTotal] = useState(0);

useEffect(() => {
setTotal(gastos.reduce((s, g) => s + g.valor, 0));
}, [gastos]);
// Problemas: un render extra (React pinta, luego el efecto cambia 'total', repinta),
// dos fuentes de verdad que se pueden desincronizar, y código de más.

La cura es no tener el estado en absoluto: DERIVA el total en el render. Este playground muestra la forma correcta —sin efecto, sin estado extra—:

total y caros se CALCULAN en cada render a partir de gastos. No son estado, no hay efecto, no hay desincronización posible: si gastos cambia, el componente re-renderiza y los valores se recalculan solos. Menos código, un render menos, cero bugs. Esta es la misma lección de "lo derivado se calcula, no se guarda" que viste en el reto del módulo 2 —ahora sabes que meterlo en un efecto es el error concreto que hay que evitar—.

Error nº2: responder a un clic con un efecto

Cuándo SÍ es un efecto

Tienes const [items, setItems] = useState([]) y quieres mostrar cuántos items hay. ¿Cuál es la forma correcta?

Mini-reto

Elimina un efecto innecesario: 1) parte de un componente con const [texto, setTexto] = useState("") y un input controlado; 2) muestra la cantidad de caracteres y si el texto es "largo" (más de 10): hazlo DERIVANDO en el render (const largo = texto.length > 10), sin ningún useEffect ni estado extra; 3) agrega un botón "Copiar" que en su onClick (no en un efecto) haga algo con el texto y muestre un aviso. Confirma que no escribiste ni un solo useEffect —y que todo funciona—.

Qué sigue

Ya sabes cuándo un efecto sobra. Ahora el otro hook del módulo: useRef, la "caja" que React NO observa. Sirve para dos cosas muy distintas —referenciar un elemento del DOM (enfocar un input, medirlo) y guardar un valor mutable que NO dispara re-render (el id de un timer, un valor anterior)—. La próxima lección lo desarma.