Curso react nivel medio

Introducción
En React, el hook useMemo se utiliza para memorizar valores calculados, optimizando el rendimiento de componentes funcionales. Esto es especialmente útil cuando tienes cálculos complejos o costosos que no necesitan ejecutarse en cada renderización.


¿Qué es useMemo?

useMemo memoriza el resultado de una función de cálculo y solo la vuelve a ejecutar cuando cambian sus dependencias. Esto ayuda a evitar cálculos innecesarios.


Sintaxis

1const valorMemoizado = useMemo(() => {
2  return computaciónPesada();
3}, [dependencias]);
  • useMemo recibe dos argumentos:

    1. Función de cálculo: Devuelve el valor que se memorizará.
    2. Array de dependencias: Cuando alguna de estas dependencias cambia, la función se vuelve a ejecutar.
  • valorMemoizado: Es el resultado de la función de cálculo, que será reutilizado hasta que las dependencias cambien.


Caso Básico: Cálculo Costoso

Sin useMemo

1import React, { useState } from "react";
2
3function CalculoPesado({ valor }) {
4  const calcular = () => {
5    console.log("Calculando...");
6    let suma = 0;
7    for (let i = 0; i < 1000000000; i++) {
8      suma += valor;
9    }
10    return suma;
11  };
12
13  return <p>Resultado: {calcular()}</p>;
14}
15
16function App() {
17  const [contador, setContador] = useState(0);
18
19  return (
20    <div>
21      <button onClick={() => setContador(contador + 1)}>Incrementar</button>
22      <CalculoPesado valor={contador} />
23    </div>
24  );
25}
26
27export default App;

Problema

Cada vez que haces clic en "Incrementar", la función calcular se ejecuta de nuevo, incluso si no ha cambiado el valor necesario.


Con useMemo

1import React, { useState, useMemo } from "react";
2
3function CalculoPesado({ valor }) {
4  const resultadoMemoizado = useMemo(() => {
5    console.log("Calculando...");
6    let suma = 0;
7    for (let i = 0; i < 1000000000; i++) {
8      suma += valor;
9    }
10    return suma;
11  }, [valor]);
12
13  return <p>Resultado: {resultadoMemoizado}</p>;
14}
15
16function App() {
17  const [contador, setContador] = useState(0);
18
19  return (
20    <div>
21      <button onClick={() => setContador(contador + 1)}>Incrementar</button>
22      <CalculoPesado valor={contador} />
23    </div>
24  );
25}
26
27export default App;

Ventaja

La función calcular solo se ejecuta cuando valor cambia, mejorando el rendimiento.


Caso Práctico: Filtrar una Lista

Sin useMemo

1import React, { useState } from "react";
2
3function App() {
4  const [filtro, setFiltro] = useState("");
5  const usuarios = ["Ana", "Juan", "Carlos", "Sofía", "María"];
6
7  const usuariosFiltrados = usuarios.filter((usuario) =>
8    usuario.toLowerCase().includes(filtro.toLowerCase())
9  );
10
11  return (
12    <div>
13      <input
14        type="text"
15        placeholder="Buscar..."
16        value={filtro}
17        onChange={(e) => setFiltro(e.target.value)}
18      />
19      <ul>
20        {usuariosFiltrados.map((usuario, index) => (
21          <li key={index}>{usuario}</li>
22        ))}
23      </ul>
24    </div>
25  );
26}
27
28export default App;

Problema

El filtrado de la lista se recalcula en cada render, incluso si el filtro no cambia.


Con useMemo

1import React, { useState, useMemo } from "react";
2
3function App() {
4  const [filtro, setFiltro] = useState("");
5  const usuarios = ["Ana", "Juan", "Carlos", "Sofía", "María"];
6
7  const usuariosFiltrados = useMemo(() => {
8    console.log("Filtrando usuarios...");
9    return usuarios.filter((usuario) =>
10      usuario.toLowerCase().includes(filtro.toLowerCase())
11    );
12  }, [filtro]);
13
14  return (
15    <div>
16      <input
17        type="text"
18        placeholder="Buscar..."
19        value={filtro}
20        onChange={(e) => setFiltro(e.target.value)}
21      />
22      <ul>
23        {usuariosFiltrados.map((usuario, index) => (
24          <li key={index}>{usuario}</li>
25        ))}
26      </ul>
27    </div>
28  );
29}
30
31export default App;

Ventaja

La lista de usuarios solo se filtra cuando cambia el valor de filtro.


Caso Complejo: Tabla con Cálculos Derivados

Ejemplo

1import React, { useState, useMemo } from "react";
2
3function App() {
4  const [datos, setDatos] = useState([
5    { nombre: "Producto A", precio: 50, cantidad: 2 },
6    { nombre: "Producto B", precio: 30, cantidad: 4 },
7    { nombre: "Producto C", precio: 20, cantidad: 5 },
8  ]);
9
10  const total = useMemo(() => {
11    console.log("Calculando total...");
12    return datos.reduce((suma, item) => suma + item.precio * item.cantidad, 0);
13  }, [datos]);
14
15  return (
16    <div>
17      <h1>Lista de Productos</h1>
18      <ul>
19        {datos.map((item, index) => (
20          <li key={index}>
21            {item.nombre}: ${item.precio} x {item.cantidad}
22          </li>
23        ))}
24      </ul>
25      <h2>Total: ${total}</h2>
26    </div>
27  );
28}
29
30export default App;

Ventaja

El cálculo del total solo se ejecuta cuando cambian los datos.


Consideraciones

  1. useMemo No es Siempre Necesario
    Solo úsalo cuando los cálculos sean costosos y afecten el rendimiento.

  2. Evitar Arrays de Dependencias Vacíos
    Si el array de dependencias está vacío ([]), la función solo se ejecutará una vez, lo cual puede ser útil en algunos casos, pero asegúrate de que sea lo que necesitas.

  3. Memoria vs. Rendimiento
    Memorizar cálculos consume algo de memoria. Úsalo sabiamente para encontrar el balance entre rendimiento y consumo de recursos.


Conclusión

useMemo es una herramienta poderosa para optimizar el rendimiento de componentes React al evitar cálculos innecesarios. Es ideal para:

  • Cálculos complejos.
  • Filtrado y mapeo de grandes datasets.
  • Casos donde las dependencias cambian con poca frecuencia.