React 19 de Cero a Experto / Efectos y ciclo de vida
Cuándo NO usar useEffect (la lección clave)
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.
// ❌ 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.