Curso react nivel medio

Optimizar una aplicación React ayuda a mejorar el rendimiento, especialmente en aplicaciones grandes o con muchas interacciones. En esta sección, aprenderemos sobre división de código (code splitting), memorización de funciones y valores, manejo de errores y, finalmente, realizaremos un ejercicio práctico para optimizar una lista grande.

1. División de código (code splitting) y lazy loading

La división de código permite cargar solo las partes de la aplicación necesarias en cada momento, en lugar de cargar todo el código de una sola vez. Esto reduce el tiempo de carga inicial.

React.lazy permite cargar componentes de forma asíncrona (lazy loading). Esto significa que el componente se carga solo cuando se necesita. React.Suspense nos permite manejar la carga de estos componentes, mostrando un mensaje o un indicador mientras se cargan.

Ejemplo de lazy loading:

1import React, { Suspense } from 'react';
2
3// Carga perezosa del componente
4const ComponentePesado = React.lazy(() => import('./ComponentePesado'));
5
6function App() {
7  return (
8    <div>
9      <h1>Aplicación</h1>
10      <Suspense fallback={<p>Cargando componente...</p>}>
11        <ComponentePesado />
12      </Suspense>
13    </div>
14  );
15}
16
17export default App;

En este ejemplo:

  • React.lazy carga el componente ComponentePesado solo cuando se necesita, en lugar de cargarlo de inmediato.
  • Suspense envuelve el componente y muestra “Cargando componente…” mientras el componente ComponentePesado se está cargando.

2. Memorización y optimización con useMemo y useCallback

La memorización es una técnica que ayuda a evitar cálculos innecesarios o recreaciones de funciones y valores, optimizando el rendimiento.

  • useMemo: Memoriza un valor o cálculo que es costoso de realizar, evitando que se recalcule en cada renderizado, a menos que cambien sus dependencias.

Ejemplo de useMemo:

1import React, { useMemo, useState } from 'react';
2
3function App() {
4  const [numero, setNumero] = useState(0);
5
6  const calculoComplejo = useMemo(() => {
7    console.log("Realizando cálculo complejo...");
8    return numero * 2;
9  }, [numero]);
10
11  return (
12    <div>
13      <p>Resultado del cálculo: {calculoComplejo}</p>
14      <button onClick={() => setNumero(numero + 1)}>Incrementar</button>
15    </div>
16  );
17}
18
19export default App;

En este ejemplo:

  • useMemo memoriza el valor de calculoComplejo para evitar recalcularlo en cada renderizado si el valor de numero no ha cambiado.

  • useCallback: Memoriza una función, útil cuando la función se pasa como prop a otros componentes que podrían volver a renderizarse innecesariamente.

Ejemplo de useCallback:

1import React, { useCallback, useState } from 'react';
2
3function Hijo({ incrementar }) {
4  return <button onClick={incrementar}>Incrementar</button>;
5}
6
7function App() {
8  const [contador, setContador] = useState(0);
9
10  const incrementar = useCallback(() => {
11    setContador((c) => c + 1);
12  }, []);
13
14  return (
15    <div>
16      <p>Contador: {contador}</p>
17      <Hijo incrementar={incrementar} />
18    </div>
19  );
20}
21
22export default App;

Aquí, useCallback memoriza la función incrementar, evitando que se cree una nueva función en cada renderizado, lo que a su vez evita que el componente Hijo se renderice innecesariamente.

3. Suspense y Error Boundaries

Suspense permite manejar la carga de componentes de forma elegante, mostrando un componente de carga mientras el componente principal se carga.

Error Boundaries capturan errores en el renderizado de componentes y muestran un mensaje alternativo en lugar de hacer que toda la aplicación falle. Esto solo puede implementarse en componentes de clase.

Ejemplo de Error Boundary:

1import React, { Component } from 'react';
2
3class ErrorBoundary extends Component {
4  constructor(props) {
5    super(props);
6    this.state = { hasError: false };
7  }
8
9  static getDerivedStateFromError(error) {
10    return { hasError: true };
11  }
12
13  componentDidCatch(error, info) {
14    console.error("Error capturado:", error, info);
15  }
16
17  render() {
18    if (this.state.hasError) {
19      return <h2>Algo salió mal.</h2>;
20    }
21
22    return this.props.children;
23  }
24}
25
26export default ErrorBoundary;

Este ErrorBoundary mostrará el mensaje “Algo salió mal” si ocurre un error en el renderizado de un componente hijo.

4. Ejercicio práctico: Optimización de una lista grande

Para este ejercicio, crearemos una lista muy grande de elementos y optimizaremos su renderizado usando react-window, una biblioteca que permite cargar solo los elementos visibles en pantalla.

Instalación de react-window:

1npm install react-window

Código de la aplicación optimizada (ListaGrande.js):

1import React, { useMemo, useState } from 'react';
2import { FixedSizeList as List } from 'react-window';
3
4const generarItems = (cantidad) => {
5  console.log("Generando lista de items...");
6  return Array.from({ length: cantidad }, (_, index) => `Item ${index + 1}`);
7};
8
9function ListaGrande() {
10  const [cantidad] = useState(10000);
11  
12  // Memorizar la lista de items
13  const items = useMemo(() => generarItems(cantidad), [cantidad]);
14
15  return (
16    <div>
17      <h1>Lista de {cantidad} items</h1>
18      <List
19        height={400}
20        itemCount={items.length}
21        itemSize={35}
22        width="100%"
23      >
24        {({ index, style }) => (
25          <div style={style}>{items[index]}</div>
26        )}
27      </List>
28    </div>
29  );
30}
31
32export default ListaGrande;

Explicación del código:

  1. Memorización de items:

    • useMemo memoriza el array de items, evitando recalcular la lista completa a menos que cambie cantidad.
  2. Uso de react-window:

    • FixedSizeList de react-window solo renderiza los elementos visibles en pantalla.
    • Propiedades:
      • height: Altura visible de la lista.
      • itemCount: Número total de elementos.
      • itemSize: Altura de cada elemento.
      • width: Ancho de la lista.
  3. Virtualización de la lista:

    • Con react-window, solo se renderizan los elementos visibles en pantalla, mejorando el rendimiento cuando la lista es muy grande.

Este ejercicio demuestra cómo optimizar el rendimiento en React usando división de código, memorización, Suspense, y Error Boundaries para crear aplicaciones eficientes y fáciles de mantener.

  • Loading...