React Memo
Introducción
En React, React.memo es una herramienta que permite optimizar el rendimiento de los componentes funcionales al memorizar su resultado. Esto significa que un componente envuelto con React.memo solo se volverá a renderizar si cambian sus props.
1. Qué es React.memo? #
React.memo es una función de orden superior que memoriza un componente funcional. Si las props no cambian, el componente no se volverá a renderizar, incluso si el componente padre se vuelve a renderizar.
Sintaxis #
1const ComponenteMemorizado = React.memo(ComponenteOriginal);
ComponenteOriginal: El componente funcional que quieres memorizar.ComponenteMemorizado: El nuevo componente optimizado.
2. Ejemplo Básico: Sin React.memo #
Código #
1import React, { useState } from "react"; 2 3function ComponenteHijo({ mensaje }) { 4 console.log("Renderizando ComponenteHijo"); 5 return <p>{mensaje}</p>; 6} 7 8function App() { 9 const [contador, setContador] = useState(0); 10 11 return ( 12 <div> 13 <h1>React.memo: Ejemplo Básico</h1> 14 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 15 <p>Contador: {contador}</p> 16 <ComponenteHijo mensaje="Soy un componente hijo" /> 17 </div> 18 ); 19} 20 21export default App;
Problema #
Cada vez que haces clic en "Incrementar", el componente hijo (ComponenteHijo) se vuelve a renderizar, aunque sus props no cambien.
3. Con React.memo #
Código #
1import React, { useState } from "react"; 2 3const ComponenteHijo = React.memo(({ mensaje }) => { 4 console.log("Renderizando ComponenteHijo"); 5 return <p>{mensaje}</p>; 6}); 7 8function App() { 9 const [contador, setContador] = useState(0); 10 11 return ( 12 <div> 13 <h1>React.memo: Ejemplo Básico</h1> 14 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 15 <p>Contador: {contador}</p> 16 <ComponenteHijo mensaje="Soy un componente hijo" /> 17 </div> 18 ); 19} 20 21export default App;
Ventaja #
- Optimización:
ComponenteHijono se vuelve a renderizar si las props (mensaje) no cambian. console.logenComponenteHijosolo se ejecuta cuando cambian las props.
4. Caso Práctico: Tabla de Datos #
Supongamos que tienes una tabla que recibe una lista de datos y un contador independiente.
Código Sin React.memo #
1function Tabla({ datos }) { 2 console.log("Renderizando Tabla"); 3 return ( 4 <table> 5 <thead> 6 <tr> 7 <th>Nombre</th> 8 <th>Edad</th> 9 </tr> 10 </thead> 11 <tbody> 12 {datos.map((dato, index) => ( 13 <tr key={index}> 14 <td>{dato.nombre}</td> 15 <td>{dato.edad}</td> 16 </tr> 17 ))} 18 </tbody> 19 </table> 20 ); 21} 22 23function App() { 24 const [contador, setContador] = useState(0); 25 const datos = [ 26 { nombre: "Ana", edad: 25 }, 27 { nombre: "Juan", edad: 30 }, 28 ]; 29 30 return ( 31 <div> 32 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 33 <p>Contador: {contador}</p> 34 <Tabla datos={datos} /> 35 </div> 36 ); 37} 38 39export default App;
Problema #
Cada vez que el contador cambia, la tabla (Tabla) se vuelve a renderizar aunque los datos no cambien.
Solución: Con React.memo #
1const Tabla = React.memo(({ datos }) => { 2 console.log("Renderizando Tabla"); 3 return ( 4 <table> 5 <thead> 6 <tr> 7 <th>Nombre</th> 8 <th>Edad</th> 9 </tr> 10 </thead> 11 <tbody> 12 {datos.map((dato, index) => ( 13 <tr key={index}> 14 <td>{dato.nombre}</td> 15 <td>{dato.edad}</td> 16 </tr> 17 ))} 18 </tbody> 19 </table> 20 ); 21}); 22 23function App() { 24 const [contador, setContador] = useState(0); 25 const datos = [ 26 { nombre: "Ana", edad: 25 }, 27 { nombre: "Juan", edad: 30 }, 28 ]; 29 30 return ( 31 <div> 32 <button onClick={() => setContador(contador + 1)}>Incrementar</button> 33 <p>Contador: {contador}</p> 34 <Tabla datos={datos} /> 35 </div> 36 ); 37} 38 39export default App;
Ventaja #
- La tabla solo se renderiza una vez, a menos que los datos cambien.
- Esto mejora el rendimiento al evitar renderizados innecesarios.
5. Comparación de Props con React.memo #
De forma predeterminada, React.memo compara las props superficialmente (shallow comparison). Si necesitas una comparación más avanzada, puedes pasar una función de comparación personalizada como segundo argumento.
Ejemplo: Comparación Personalizada #
1const ComponenteMemorizado = React.memo( 2 ({ datos }) => { 3 console.log("Renderizando ComponenteMemorizado"); 4 return <p>{JSON.stringify(datos)}</p>; 5 }, 6 (prevProps, nextProps) => { 7 // Compara objetos profundamente 8 return JSON.stringify(prevProps.datos) === JSON.stringify(nextProps.datos); 9 } 10);
6. Consideraciones Importantes #
-
React.memono optimiza siempre:
Solo es útil si los renderizados adicionales son costosos o afectan el rendimiento. En aplicaciones simples, podría no ser necesario. -
Evitar Comparaciones Complejas:
Si tus props son complejas, intenta estructurarlas mejor o usar un estado global. -
No Memorizar en Exceso:
No envuelvas todos los componentes conReact.memo, ya que podría complicar el código sin mejorar mucho el rendimiento.
7. Caso Avanzado: React.memo con Callbacks (useCallback) #
Si pasas funciones como props a un componente memorizado, usa useCallback para evitar que React las trate como nuevas en cada render.
1import React, { useState, useCallback } from "react"; 2 3const Boton = React.memo(({ onClick, texto }) => { 4 console.log(`Renderizando Botón: ${texto}`); 5 return <button onClick={onClick}>{texto}</button>; 6}); 7 8function App() { 9 const [contador, setContador] = useState(0); 10 11 const incrementar = useCallback(() => { 12 setContador((prev) => prev + 1); 13 }, []); 14 15 return ( 16 <div> 17 <h1>Contador: {contador}</h1> 18 <Boton onClick={incrementar} texto="Incrementar" /> 19 </div> 20 ); 21} 22 23export default App;
Ventaja #
- El botón no se vuelve a renderizar porque la función
incrementarestá memorizada conuseCallback.
Resumen del tema
Conceptos clave #
- Componentes de Orden Superior (HOC):
React.memo(Componente)envuelve un componente funcional para omitir su re-renderizado si sus props no han cambiado. - Comparación Superficial (Shallow Comparison): compara props por valor en primitivos y por referencia de memoria en objetos, funciones y arrays.
- Sinergia con
useCallbackyuseMemo: para queReact.memosea efectivo con props de tipo función u objeto, estas deben memorizarse previamente en el padre conuseCallbackouseMemo. - Función Comparadora Personalizada: segundo parámetro opcional
React.memo(Comp, (prevProps, nextProps) => boolean)para evaluar igualdad personalizada.
Qué debes recordar #
React.memo evita que un componente hijo se re-renderice cuando su padre cambia, siempre que sus props mantengan la misma referencia o valor.