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: ComponenteHijo no se vuelve a renderizar si las props (mensaje) no cambian.
  • console.log en ComponenteHijo solo 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

  1. React.memo no optimiza siempre:
    Solo es útil si los renderizados adicionales son costosos o afectan el rendimiento. En aplicaciones simples, podría no ser necesario.

  2. Evitar Comparaciones Complejas:
    Si tus props son complejas, intenta estructurarlas mejor o usar un estado global.

  3. No Memorizar en Exceso:
    No envuelvas todos los componentes con React.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 incrementar está memorizada con useCallback.

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.