¿Por qué se vuelve a renderizar un componente?
React vuelve a renderizar un componente cuando:
- Cambia su estado (un
setState). - Cambian sus props.
- Se re-renderiza su componente padre (¡aunque sus props no cambien!).
Ese tercer punto es clave: por defecto, si un padre re-renderiza, todos sus hijos re-renderizan en cascada. Casi siempre eso es barato y no pasa nada. Pero cuando un subárbol es pesado, conviene tener herramientas para evitar trabajo innecesario.
React.memo: memoiza un componente
React.memo envuelve un componente para que solo se re-renderice si sus
props cambian (comparación superficial). Si el padre repinta pero las props
del hijo son las mismas, React reutiliza el render anterior:
const ListaPesada = React.memo(function ListaPesada({ items }) {
// render costoso...
return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;
});
Solo es útil si las props se mantienen estables entre renders. Ahí entran los dos hooks siguientes.
useMemo: memoiza un valor calculado
useMemo recuerda el resultado de un cálculo y solo lo recalcula si
cambia alguna de sus dependencias. Ideal para cálculos caros:
const ordenados = useMemo(() => {
return [...items].sort((a, b) => a - b); // cálculo caro
}, [items]); // solo se recalcula si 'items' cambia
Sin useMemo, ese sort correría en cada render. Con él, solo cuando
items cambia.
useCallback: memoiza una función
En cada render se crean funciones nuevas. Si pasas una función como prop a
un componente envuelto en React.memo, esa prop "cambia" siempre y la
memoización se rompe. useCallback estabiliza la identidad de la función:
const manejarClick = useCallback(() => {
setSeleccion(id);
}, [id]); // misma función mientras 'id' no cambie
useCallback(fn, deps) es equivalente a useMemo(() => fn, deps): uno
memoiza la función, el otro su resultado.
Regla mental:
useMemopara valores,useCallbackpara funciones,React.memopara componentes. Los tres comparan dependencias/props para decidir si pueden saltarse trabajo.
Ejemplos
useMemo evita recalcular si las dependencias no cambian
let calculos = 0;
function calcularCaro(n) { calculos++; return n * 2; }
// Render 1
const a = calcularCaro(21);
// Render 2 con la misma entrada -> con useMemo NO se recalcularía
console.log("resultado:", a, "| veces calculado:", calculos);