Curso react nivel medio
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.
¿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.
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.
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.
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.
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);
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.
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.
Conclusión
React.memo es una herramienta poderosa para optimizar componentes funcionales al evitar renderizados innecesarios. Úsalo sabiamente en:
- Componentes con renderizados costosos.
- Componentes que reciben datos que cambian con poca frecuencia.