Leyla Carmona
Brandon Argel Verdeja Domínguez
La optimización de componentes en React es una habilidad esencial para desarrolladores que buscan crear aplicaciones eficientes y de alto rendimiento. Mediante el uso de herramientas específicas como React.memo, useCallback y useMemo, podemos evitar renderizados innecesarios y mejorar significativamente el rendimiento de nuestras aplicaciones, especialmente cuando trabajamos con componentes complejos o cálculos intensivos.
¿Cómo optimizar componentes con React.memo?
React.memo es una función de orden superior (HOF) que nos permite optimizar el rendimiento de nuestros componentes funcionales. Su principal beneficio es que evita re-renderizados innecesarios, ya que solo permite que un componente se vuelva a renderizar cuando sus props cambian.
Para demostrar su funcionamiento, vamos a crear un ejemplo sencillo con un contador:
import { useState } from 'react'; const Child = ({ counter }) => { console.log("renderizando child"); return <p>Contador: {counter}</p>; }; const Parent = () => { const [counter, setCounter] = useState(0); return ( <div> <button onClick={() => setCounter(prev => prev + 1)}> Incrementar </button> <Child counter={counter} /> </div> ); }; export default Parent;
En este ejemplo, cada vez que hacemos clic en el botón "Incrementar", el componente Child se renderiza nuevamente. Esto es correcto cuando la prop counter cambia, pero si tuviéramos otras interacciones en el componente padre que no afectan a counter, el componente hijo se seguiría renderizando innecesariamente.
Para optimizar esto, aplicamos React.memo:
import { useState } from 'react'; import React from 'react'; const Child = React.memo(({ counter }) => { console.log("renderizando child"); return <p>Contador: {counter}</p>; }); // El resto del código permanece igual
Ahora, el componente Child solo se renderizará cuando la prop counter cambie, evitando renderizados innecesarios cuando otras partes del componente padre se actualicen.
¿Cómo optimizar funciones con useCallback?
Cuando pasamos funciones como props a componentes optimizados con React.memo, podemos enfrentar un problema: las funciones se recrean en cada renderizado del componente padre, lo que hace que React.memo no funcione correctamente.
Para solucionar esto, usamos useCallback:
import { useState, useCallback } from 'react'; import React from 'react'; const Parent = () => { const [counter, setCounter] = useState(0); const increment = useCallback(() => { setCounter(prev => prev + 1); }, []); return ( <div> <button onClick={increment}> Incrementar </button> <Child counter={counter} /> </div> ); };
Con useCallback, la función increment se mantiene estable entre renderizados, lo que significa que no se crea una nueva función cada vez que el componente padre se renderiza. Esto es especialmente útil cuando pasamos esta función como prop a componentes optimizados con React.memo.
¿Cómo optimizar cálculos costosos con useMemo?
Para operaciones o cálculos intensivos que no necesitan recalcularse en cada renderizado, podemos utilizar useMemo:
import { useState, useMemo } from 'react'; function ExpensiveCalculation({ num }) { const result = useMemo(() => { console.log("Se está calculando"); return num * 2; }, [num]); return <p>Resultado: {result}</p>; } const Parent = () => { const [counter, setCounter] = useState(0); return ( <div> <button onClick={() => setCounter(prev => prev + 1)}> Incrementar </button> <Child counter={counter} /> <ExpensiveCalculation num={counter} /> </div> ); };
En este ejemplo, el cálculo dentro de useMemo solo se ejecutará cuando la dependencia num cambie. Si otros estados o props del componente cambian pero num permanece igual, se utilizará el resultado memorizado en lugar de recalcular.
Esta optimización es particularmente valiosa para:
La optimización de componentes en React es un equilibrio entre rendimiento y complejidad. Estas herramientas nos permiten mejorar significativamente la eficiencia de nuestras aplicaciones, pero es importante aplicarlas estratégicamente donde realmente se necesitan. ¿Has implementado alguna de estas técnicas en tus proyectos? Comparte tu experiencia en los comentarios.
Leyla Carmona
Brandon Argel Verdeja Domínguez
José Antonio Sterling Sánchez
Alejandro Esaú Maximino Olmedo
Santiago Perozo
Alfonso Neil Jiménez Casallas
Hector Emanuel Barboza
Diana Flores
Jorge Arias Argüelles
Max Andy Diaz Neyra
lucas cartisano
Santiago Gonzalez
Jorge Luis Rueda Marin
Jose Luis Bedoya
Fernando Labastida Vázquez
Kevin Toruño
Agustina Corvo
Eldrich Daniel Raygoza Díaz
Carlos Hernandez
Neil Ruiz
Sebas Ch
Luis Eduardo Payano Villar
Harold Zurita Simon
Daniel Alberto Vega Bejarano
Jose Luis Bedoya
Francisco Flores Camarillo
Alvaro H. Ruiz V.
Leonel Moyano
Edwin Einsen Vásquez Velásquez
Emilio Nicolás Mendoza Patti
¡Hola, Leyla! Muchas gracias por tu aporte, solo paso a hacer una pequeña corrección. El termino correcto en este caso es memoización sin la "r", o memoization en inglés.
Esta clase en particular, no fue nada clara, no se entiende para que se usa useMemo, que diferencia hay entre usarlo y no usarlo. Que ventajas me ofrece esto (No se ve), Fue una clase perdida.
Es bueno saber que no fui el unico que no le quedo claro el uso de React.memo
React.memo memoriza las props de un componente solo si son un valor primitivo (number, string, boolean, etc). useCallback memoriza las props de un componente si son un tipo de dato mas complejo, como un objeto o una función como vimos en el ejemplo de la clase. useMemo memoriza cálculos costosos, evitando recalcular innecesariamente. Todas evitan renders innecesarios, de esta manera optimizando la aplicación.
:p
Esa fue mi cara cuando de la nada aparecio:
<Child counter={counter}/>
🔥
React.memo:
const Child = React.memo(({ counter }) => { console.log('renderizando child'); return <p>Contador: {counter}</p>; });
useCallback:
const increment = useCallback(() => { setCounter(prev => prev + 1); }, []);
useMemo:
const result = useMemo(() => expensiveCalculation(counter), [counter]);
Estos conceptos son fundamentales para optimizar aplicaciones React y mejorar su rendimiento.
Magnfico resumen
Tengo la sensación que no explica nada, solo relata lo que va haciendo. Sigo sin entender, que es, de donde viene, como funciona, cuando usarlo y por que
Sí, sólo va narrando y ya
Tengo la misma percepción
memo es muy poderoso! SI quieren un ejemplo más práctico:
Agreguen un nuevo estado que cambie cuando presionen otro botón, no el de aumentar, sin react memo, cada vea que ese boton sea presionado, el componente hijo se renderizará.
Pero con react memo no lo hará
Jamás se mostraron los beneficios de usar memo y callback, sólo se pusieron ejemplos de primaria sin mostrar el uso de los recursos... ni tiempos de ejecución... Nada. Pésima clase, deberían actualizarla
Siento que el principal problema de esta clase fue que no se puede ver el beneficio real de evitar renderizados con este escenario tan simple en donde solo hay un unico estado. El beneficio de React.memo parece ser cuando hay multiples estados en el mismo componente.
Esto me quedo mucho más claro gracias a la explicacion en el apartado de Recursos, ahí esta mucho mejor explicado que en el video en sí jajaja. Les recomiendo leer toda la descripcion del apartado de Recursos.
Qué utillidad tiene useCalback en este ejemplo?
Quizás en este ejemplo no se nota la diferencia, ya que cuando hacemos clic en el botón se ejecuta la función "increment" que cambia el valor del estado "counter" y el Child precisamente se va renderizar cuando ese estado cambie.
Pero imagina una situación donde increment( )**** es usado como prop de otro componente. Por dar un ejemplo, algo asi:
<Incrementar fun={increment} />
Aqui si hay una diferencia, ya que cada vez que el componente padre se renderice por cualquier cambio que le afecte como una actualización de estado e increment( ) no tiene useCallback( ) lo que va pasar es que React va querer guardar la función otra vez. Cuando eso suceda se lo va a pasar como prop a Incrementar como algo nuevo, e independientemente de que si Incrementar tenga React.memo o no lo va a renderizar otra vez aun si increment( ) no haya afectado el cambio de un estado dentro de Incrementar.
Y aquí useCallback( ) evita eso, al guardar esa función y "recordarle" a React que ya existe y no hay razon para crearla de nuevo. :D
No queda nada claro cuál es la diferencia de utilizar el componente envuelto con memo a usarlo sin memo.
Tampoco se explica bien cuál es la utilidad de useCallback... de hecho, mi editor se queja de que hay que pasarle un array de dependencias.
Realmente las explicaciones son muy básicas y no se entiende bien el caso de uso
Coincido con las críticas realizadas a esta pésima clase de este mediocre curso, pero a quienes les interese, les comparto algunos puntos del análisis que realicé por mi cuenta para entender los conceptos que se mencionan en este vídeo.
El primero es el asociado al concepto de “función de orden superior” el cual corresponde a un término de JavaScript para referirse a las funciones que cumplen con las características de recibir una función como parámetro y devolver una función como resultado.
Este concepto se trasladó a React con el nombre de High Orden Component o HOC, que es un caso particular en donde una función toma un componente como argumento y devuelve un nuevo componente.
Este es justamente el caso de React.memo que en el ejemplo del vídeo utiliza como entrada una función anónima que representa un componente normal, ya que recibe la prop llamada counter y retorna JSX que corresponde a la etiqueta p, que aparece en la línea 5 la siguiente imagen:
Las 3 herramientas mencionadas en este vídeo que corresponden a React.memo y los hooks useCallback y useMemo se utilizan para reducir las renderizaciones innecesarias haciendo que el código sea más eficiente, pero esto no se percibe en el ejemplo que se desarrolla hasta el minuto 6:40, ya que la prop counter cambia en cada clic, haciendo que Child se re-renderice.
Para entender el ahorro que aporta React.memo se debe construir un escenario en el que el componente padre se re-renderice y el componente hijo no lo haga, lo que requiere que durante el proceso de re-renderización del padre las props del componente hijo (en el vídeo se llama Child) sean iguales.
Este escenario lo obtuve ajustando el código del vídeo como se ve en la siguiente imagen, en el que agregué un estado adicional llamado “tick” (línea 10) y un botón para modificarlo (línea 13), ambos asociados al componente padre que en este caso llamé “MemoCounter”.
Lo que hace StrictMode en el ambiente de desarrollo es montar y desmontar los componentes dos veces de forma intencional y esto se hace para detectar efectos secundarios inesperados en los componentes y por eso en este ejemplo se ve el console.log("Renderizando Child") duplicado. Al trabajar en producción empleando el comando “npm run build” en la terminal, esto deja de suceder
Si se utiliza el botón de la izquierda se incrementará el valor de la variable/estado del componente padre a la que llamé “tick”, haciendo que dicho componente se re-renderice, sin renderizar el componente hijo, lo cual se puede evidenciar porque el console.log("Renderizando Child") no se vuelve a imprimir. Es decir, me ahorré el tiempo de re-renderizado del componente hijo que en un proyecto más complejo puede impactar el rendimiento de forma significativa.
Al igual que React.memo, el hook useCallback también se emplea para evitar renderizaciones innecesarias memorizando funciones, las cuales se pasan como props al componente hijo, en lugar de usar valores.
Debido a que el ejemplo utilizado para explicar el funcionamiento de useCallback está basado en el código que se construyó hasta el minuto 6:40 del vídeo, el cual sólo sirve para conocer la sintaxis de React.memo, no es posible ver con claridad la utilidad que tiene este hook en el código.
Por lo anterior decidí crear un archivo con una versión de este contador utilizando el hook useCallback, el cual es prácticamente el mismo que les mostré anteriormente con el que analicé el funcionamiento de React.memo, incluyendo algunos ajustes que se pueden ver en la siguiente imagen:
En esta oportunidad el componente hijo al que se le dio el nombre de “Child”, recibe una prop llamada onIncrement como se ve en la línea 3, la cual toma su valor del atributo utilizado en la etiqueta personalizada del componente “Child” que aparece en la línea 17.
Dicho atributo onIncrement toma su valor de una función llamada “handleIncrement”, la cual realiza la modificación del estado que muestra el componente hijo, pero dicha función se ubica en el cuerpo de la función que define el componente padre (líneas 11 - 13), que en este caso se llama “CallbackCounter”.
Esto significa que el cambio de este estado counter también se va a realizar en el componente padre, pero a través de acciones realizadas en el componente hijo, que en este caso recibe como prop una función, en lugar de recibir el valor registrado en la variable/estado counter.
Esta opción de pasar funciones en lugar de valores como props se utiliza en situaciones en las que el componente hijo debe disparar acciones que afectan al estado del padre como por ejemplo en el formulario de comentarios de un blog en el que el componente hijo está representado por el espacio que tienen los usuarios para escribir su aporte y un botón para realizar el envío del mismo.
En este ejemplo del blog, el componente padre puede estar representado por el listado de comentarios y la actualización del estado asociado a estos, de tal forma que el componente hijo recibe como prop una función que permite agregar el comentario al listado, cuando el usuario hace clic en el botón de envío.
La razón por la que utilicé el hook useCallback para memorizar la función que asigna el valor a la variable “handleIncrement” a pesar de ya estar utilizando React.memo, se debe a la suma de 2 factores. El primero de ellos es la forma en la que opera React, el cual recrea cada función en los render que ejecute y el segundo es que se está pasando justamente esta función como prop en el componente hijo.
La combinación de React.memo y useCallback cuando se emplean funciones como props, es un escenario ideal y muy utilizado, que en este caso hace que React.memo evite que el componente hijo (“Child”) se re-renderice si sus props no cambiaron, mientras que useCallback memoriza la función que modifica el estado (handleIncrement) evitando que cada render la recree.
Dado que React recrea en cada render esta función “handleIncrement” que es la que realiza la modificación de la variable/estado counter, lo que a su vez hace que el componente hijo se re-renderice, es necesario envolverla dentro del hook useCallback como sucede en la línea 11, utilizando un array de dependencias vacío como se ve en la línea 13.
Este array de dependencias vacío corresponde al segundo parámetro de este hook useCallback y se emplea para indicar que la función handleIncrement nunca cambia de referencia entre renders, lo que significa que, aunque cambie el estado counter, la prop onIncrement que recibe el Child sigue siendo la misma función y por lo tanto, el hijo no se re-renderiza y no aparece el console.log("Renderizando Child") otra vez.
Dado que el console.log ya no aporta valor funcional, solo queda como herramienta de depuración y en la práctica puede ser eliminado en la versión final del código o se puede conservar con fines de aprendizaje o de análisis, para establecer cómo cambian los renders según se ajusten las props y dependencias.
Esto no significa que useCallback deba usarse siempre acompañado por React.memo, ya que el objetivo de este hook es el de memorizar funciones, las cuales podrían utilizarse como dependencias de un useEffect y en este caso useCallback evitaría que el efecto se ejecute innecesariamente en cada render.
Muy regular esta clase, espero la mejoren.
En React, “optimizar” casi siempre significa reducir renders innecesarios y evitar recalcular cosas costosas cuando no cambió la data que realmente importa.
La caja de herramientas típica:
1) Entender el problema: por qué se re-renderiza
Un componente hijo se re-renderiza cuando:
La optimización consiste en que el hijo no vuelva a renderizarse si sus props son equivalentes y su UI no cambia.
1️⃣ React.memo
Ejemplo:
import React from "react"; const Child = React.memo(({ name }) => { console.log("Child render"); return <div>Hello, {name}</div>; }); function App() { const [count, setCount] = React.useState(0); return ( <div> <Child name="Alice" /> <button onClick={() => setCount(count + 1)}>Increment {count}</button> </div> ); } export default App;
✅ Qué pasa: Cada vez que presionas el botón, App se re-renderiza, pero Child no se vuelve a renderizar, porque su prop name no cambió.
2️⃣ useMemo
Ejemplo:
import React, { useState, useMemo } from "react"; function App() { const [count, setCount] = useState(0); const [text, setText] = useState(""); // Cálculo pesado const expensiveCalculation = useMemo(() => { console.log("Calculating..."); return count * 2; }, [count]); // Solo se recalcula si count cambia return ( <div> <p>Result: {expensiveCalculation}</p> <button onClick={() => setCount(count + 1)}>Increment {count}</button> <input value={text} onChange={(e) => setText(e.target.value)} /> </div> ); } export default App;
✅ Qué pasa: Si cambias el text, la función no se recalcula, porque count no cambió. Si no usaras useMemo, se recalcularía en cada render.
3️⃣ useCallback
useMemo, pero en vez de memorizar valores, memoriza funciones.React.memo. Así evitas que la función sea una nueva referencia en cada render, lo que causaría que el hijo se vuelva a renderizar innecesariamente.Ejemplo:
import React, { useState, useCallback } from "react"; const Child = React.memo(({ onClick }) => { console.log("Child render"); return <button onClick={onClick}>Click me</button>; }); function App() { const [count, setCount] = useState(0); // Memorizar la función const handleClick = useCallback(() => { console.log("Clicked!"); }, []); // Se mantiene igual siempre return ( <div> <Child onClick={handleClick} /> <button onClick={() => setCount(count + 1)}>Increment {count}</button> </div> ); } export default App;
✅ Qué pasa: Aunque App se re-renderice al cambiar count, Child no se vuelve a renderizar, porque la función handleClick no cambió (gracias a useCallback).
React.memo => Para memorizar componentes hijos y no se rendericen en el padre nuevamente.
useMemo => Para guardar el resultado de una función y no se renderice sino cambia el resultado.
useCallback => Para guardar las referencias de una función (valores o variables fuera de la función) para que no se renderice la función innecesariamente.
Este funcionamiento es muy parecido a las signals de angular o solidjs
Por favor no improvisen con los cambios de nombres de funciones, eso de estar renombrando hace que se pierda el hilo de la clase y se percibe como que la clase no fue planeada.
Se que hizo su mayor intento de explicar, pero la demostración en el ahorro de computo al realizar los cálculos quedo pendiente... o no lo entendí.
No entendí nada y me perdí bastante, solo dice "hacemos esto", "movemos esto de acá para acá", "implementamos esto" y ninguna explicación de que hace, como funciona, si es que mejora realmente.
Cuando muesta el useCallBack dice, "implementamos el useCallBack, metemos esta función dentro, esto va a ejecutar esta funcion y esto nos permite optimizar la función que tenemos acá. Esto nos permite ahorrar ciertos cosos o ciertas cosas que hagamos y así tenemos el useCallBack de manera sencilla"
Te aconsejo que tomes unos cursos de JavaScript primero para que tengas claros conceptos como "callback", y no te pierdas en la explicación.
Estoy haciendo un repaso rapido de React y me doy cuenta que nunca use memo porque no lo entendia bien. Incluso aqui hasta el minuto 6 no entendi la diferencia con el ejemplo pero justo consultando con claude pude entender que si el padre cambia y el hijo no tiene React.memo entonces el hijo tambien cambia:
Dejo este codigo para que lo prueben cambian algo del padre (input) y el hijo se renderiza de nuevo haciendo que muestre muchos logs.
import { useState } from "react" const Child =({counter}) => { console.log("Renderizando child") return <p>Contador: {counter}</p> } function CounterWithReactMemo() { const [counter, setCounter] = useState(0); const [text, setText] = useState("") return ( <div> <button className="pr-4" onClick={() => setCounter(prev => prev + 1)}> Increment </button> <input value={text} onChange={e => setText(e.target.value)} /> <Child counter={counter}/> </div> ) } export default CounterWithReactMemo