React es una de las bibliotecas de JavaScript más populares para construir interfaces de usuario. Al crear estas interfaces, es posible que necesites ejecutar acciones secundarias, como obtener datos de una API, suscribirte a eventos o manipular el DOM.

Ahí es donde entra en juego el potente Hook useEffect. Te permite manejar sin problemas estas acciones secundarias de forma declarativa y eficiente, asegurando que tu interfaz de usuario se mantiene sensible y actualizada.

Tanto si eres nuevo en React como si eres un desarrollador experimentado, comprender y dominar useEffect es esencial para crear aplicaciones robustas y dinámicas. En este artículo, aprenderás cómo funciona el Hook useEffect y cómo utilizarlo en tu proyecto React.

¿Qué es el Efecto Secundario (Side-Effect) en React?

Cuando trabajamos con componentes React, hay ocasiones en las que necesitamos interactuar con entidades o realizar acciones fuera del ámbito de React. Estas interacciones externas se conocen como efectos secundarios.

En React, la mayoría de los componentes son funciones puras, lo que significa que reciben entradas (props) y producen salidas predecibles (JSX), como se ve en el siguiente ejemplo:

export default function App() {
  return <User userName="JaneDoe" />   
}
  
function User(props) {
  return <h1>{props.userName}</h1>; // John Doe
}

Sin embargo, los efectos secundarios no son predecibles porque implican interacciones fuera del ámbito habitual de React.

Considera un ejemplo en el que quieras cambiar dinámicamente el título de la pestaña del navegador para mostrar el userName del usuario. Aunque puede resultar tentador hacerlo directamente dentro del componente, no es el enfoque recomendado porque se considera un efecto secundario:

const User = ({ userName }) => {
  document.title = `Hello ${userName}`; // ❌Never do this in the component body — It is a side effect.

  return <h1>{userName}</h1>;
}

Realizar efectos secundarios directamente dentro del cuerpo del componente puede interferir con el proceso de renderizado de nuestro componente React.

Para evitar interferencias, debes separar los efectos secundarios para que sólo se rendericen o funcionen después de que nuestro componente se haya renderizado, asegurando una clara separación entre el proceso de renderizado y cualquier interacción externa necesaria. Esta separación se realiza con el Hook useEffect.

Comprender los Fundamentos de useEffect

El Hook useEffect está diseñado para imitar los métodos del ciclo de vida como componentDidMount, componentDidUpdate, y componentWillUnmount que se encuentran en los componentes de clase.

Para utilizar useEffect, tendrás que importarlo de «react» y luego llamarlo dentro de un componente de función (en el nivel superior del componente). Toma dos argumentos: una función de devolución de llamada y un array de dependencias opcional.

useEffect(callbackFn, [dependencies]);

Esto puede escribirse mejor como

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);
  • La función de devolución de llamada contiene el código que se ejecutará cuando el componente se renderice o cambie el valor de la dependencia. Aquí es donde se ejecutan los efectos secundarios.
  • La array de dependencias especifica los valores que deben supervisarse para detectar cambios. La función de llamada de retorno se ejecutará cuando cambie cualquier valor de esta array.

Por ejemplo, ahora puedes corregir el ejemplo anterior para realizar el efecto secundario correctamente dentro de un Hook useEffect:

import { useEffect } from 'react';

const User = ({ userName }) => {
  useEffect(() => {
    document.title = `Hello ${userName}`;
  }, [userName]);
    
  return <h1>{userName}</h1>;   
}

En el ejemplo anterior, el Hook useEffect será llamado después de que el componente se haya renderizado y siempre que la dependencia — el valor de userName‘ — cambie.

Trabajar con Dependencias en useEffect

Las dependencias desempeñan un papel crucial en el control de la ejecución de useEffect. Es el segundo argumento del Hook useEffect.

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);

Utilizar un array de dependencias vacío [] garantiza que el efecto se ejecute sólo una vez, simulando componentDidMount. Especificar correctamente las dependencias permite que el efecto se actualice cuando cambian valores específicos, de forma similar a componentDidUpdate.

Nota: Debes tener cuidado al tratar con dependencias complejas. Se pueden evitar actualizaciones innecesarias seleccionando cuidadosamente qué valores incluir en el array de dependencias.

Omitir por completo el array de dependencias hará que el efecto se ejecute cada vez que se ejecute el componente, lo que puede provocar problemas de rendimiento.

useEffect(() => {
  // code to run when the effect is triggered
});

En React, comprender cómo funciona la renderización es una gran ventaja porque podrás conocer la importancia del array de dependencias.

¿Cómo Funciona la Renderización en React?

En React, la renderización genera la interfaz de usuario (UI) basándose en el estado actual de un componente y en sus props. Hay diferentes escenarios en los que se produce la renderización. El renderizado inicial ocurre cuando un componente se renderiza o monta por primera vez.

Aparte de esto, un cambio en state o props de un componente desencadena una nueva renderización para garantizar que la IU refleja los valores actualizados. Las aplicaciones React se construyen con una estructura de componentes en forma de árbol, formando una jerarquía. React parte del componente root durante la renderización y renderiza recursivamente sus componentes hijos.

Esto significa que todos los componentes se renderizarán si se produce un cambio en el componente root. Es importante tener en cuenta que llamar a los efectos secundarios (que la mayoría de las veces son funciones caras) en cada renderización puede ser costoso. Para optimizar el rendimiento, puedes utilizar el array de dependencias en el Hook useEffect para especificar cuándo debe activarse, limitando las repeticiones de renderizado innecesarias.

Uso Avanzado de useEffect: Limpieza de Efectos Secundarios

El Hook useEffect nos permite realizar efectos secundarios y proporciona un mecanismo para limpiar esos efectos secundarios. Esto garantiza que los recursos o suscripciones creados durante el efecto secundario se liberen correctamente y evita fugas de memoria.

Exploremos cómo puedes limpiar los efectos secundarios utilizando el Hook useEffect:

useEffect(() => {
  // Perform some side effect

  // Cleanup side effect
  return () => {
    // Cleanup tasks
  };
}, []);

En el fragmento de código anterior, la función de limpieza se define como un valor de retorno dentro del Hook useEffect. Esta función se invoca cuando el componente está a punto de desmontarse o antes de que se produzca una nueva renderización. Te permite limpiar cualquier recurso o suscripción establecidos durante el efecto secundario.

Aquí tienes algunos ejemplos de uso avanzado del Hook useEffect para limpiar efectos secundarios:

1. Limpiar Intervalos

useEffect(() => {
    const interval = setInterval(() => {
        // Perform some repeated action
    }, 1000);
    return () => {
        clearInterval(interval); // Clean up the interval
    };
}, []);

En este ejemplo, configuramos un intervalo que realiza una acción cada segundo. La función de limpieza borra el intervalo para evitar que se ejecute después de desmontar el componente.

2. Limpieza de Receptores de Eventos

useEffect(() => {
    const handleClick = () => {
        // Handle the click event
    };

    window.addEventListener('click', handleClick);

    return () => {
        window.removeEventListener('click', handleClick); // Clean up the event listener
    };
}, []);

Aquí creamos un receptor de eventos para el evento clic en el objeto ventana. La función de limpieza elimina el receptor de eventos para evitar fugas de memoria y garantizar una limpieza adecuada.

Recuerda que la función de limpieza es opcional, pero es muy recomendable limpiar cualquier recurso o suscripción para mantener una aplicación sana y eficiente.

Utilizar el Hook useEffect

El Hook useEffect te permite realizar tareas que implican interactuar con entidades o APIs externas, como APIs web como localStorage o fuentes de datos externas.

Exploremos el uso del Hook useEffect con varios escenarios:

1. Trabajar con API Web (localStorage)

useEffect(() => {
 // Storing data in localStorage
  localStorage.setItem('key', 'value');
  // Retrieving data from localStorage
  const data = localStorage.getItem('key');
  // Cleanup: Clearing localStorage when component unmount
  return () => {
    localStorage.removeItem('key');
  };
}, []);

En este ejemplo, el Hook useEffect se utiliza para almacenar y recuperar datos del localStorage del navegador. La función de limpieza garantiza que el localStorage se borre cuando se desmonta el componente (puede que éste no sea siempre un buen caso de uso, ya que tal vez quieras conservar los datos del localStorage hasta que se actualice el navegador).

2. Obtener Datos de una API Externa

useEffect(() => {
  // Fetching data from an external API
  fetch('https://api.example.com/data')
    .then((response) => response.json())
    .then((data) => {
      // Do something with the data
    });
}, []);

Aquí se utiliza el Hook useEffect para obtener datos de una API externa. Los datos obtenidos se pueden procesar y utilizar en el componente. No es obligatorio añadir siempre una función de limpieza.

Otros Efectos Secundarios Populares

El Hook useEffect puede utilizarse para otros efectos secundarios, como por ejemplo

A. Suscribirse a Eventos:

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

B. Modificar el Título del Documento:

useEffect(() => {
  document.title = 'New Title';
  return () => {
    document.title = 'Previous Title';
  };
}, []);

C. Gestionar los Temporizadores:

useEffect(() => {
  const timer = setInterval(() => {
    // Do something repeatedly
  }, 1000);
  return () => {
    clearInterval(timer);
  };
}, []);

Errores Comunes de useEffect y Cómo Evitarlos

Mientras trabajas con el Hook useEffect en React, es posible que te encuentres con errores que pueden provocar un comportamiento inesperado o problemas de rendimiento.

Comprender estos errores y saber cómo evitarlos puede ayudar a garantizar un uso fluido y sin errores de useEffect.

Exploremos algunos errores comunes de useEffect y sus soluciones:

1. Array de Dependencia Ausente

Un error común es olvidar incluir un array de dependencia como segundo argumento del Hook useEffect.

ESLint siempre marcará este error como advertencia, ya que puede provocar comportamientos no deseados, como una reutilización excesiva o datos obsoletos.

useEffect(() => {
  // Side effect code
}); // Missing dependency array

Solución: Proporciona siempre un array de dependencias a useEffect, aunque esté vacío. Incluye todas las variables o valores de los que depende el efecto. Esto ayuda a React a determinar cuándo debe ejecutarse o saltarse el efecto.

useEffect(() => {
  // Side effect code
}, []); // Empty dependency array or with appropriate dependencies

2. Array de Dependencia Incorrecto

Proporcionar un array de dependencias incorrecto también puede dar lugar a problemas. Si el array de dependencia no está definido con precisión, puede que el efecto no se ejecute cuando cambien las dependencias previstas.

const count = 5;
const counter = 0;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Incorrect dependency array

Solución: Asegúrate de incluir todas las dependencias necesarias en el array de dependencia. Si el efecto depende de varias variables, inclúyelas todas para activar el efecto cuando cambie alguna de las dependencias.

const count = 5;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Correct dependency array

3. Bucles Infinitos

La creación de un bucle infinito puede ocurrir cuando el efecto modifica un estado o prop que también depende del propio efecto. Esto hace que el efecto se active repetidamente, provocando un re renderizado excesivo y, potencialmente, la congelación de la aplicación.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Modifying the dependency 'count' inside the effect
}, [count]); // Dependency array includes 'count'

Solución: Asegúrate de que el efecto no modifica directamente una dependencia incluida en su array de dependencia. En su lugar, crea variables independientes o utiliza otras técnicas de gestión del estado para manejar los cambios necesarios.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount((prevCount) => prevCount + 1); // Modifying the 'count' using a callback
}, []); // You can safely remove the 'count' dependency

4. Olvidar la Limpieza

Descuidar la limpieza de los efectos secundarios puede provocar fugas de memoria o un consumo innecesario de recursos. No limpiar los escuchadores de eventos, los intervalos o las suscripciones puede provocar un comportamiento inesperado, especialmente cuando el componente se desmonta.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  // Missing cleanup
  return () => {
    clearInterval(timer); // Cleanup missing in the return statement
  };
}, []);

Solución: Proporciona siempre una función de limpieza en la sentencia de retorno del Hook useEffect.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  return () => {
    clearInterval(timer); // Cleanup included in the return statement
  };
}, []);

Si eres consciente de estos errores comunes de useEffect y sigues las soluciones recomendadas, podrás evitar posibles errores y garantizar un uso correcto y eficaz del Hook useEffect en tus aplicaciones React.

Resumen

El Hook useEffect de React es una potente herramienta para gestionar efectos secundarios en componentes de funciones. Ahora que tienes un conocimiento más profundo de useEffect, es hora de aplicar tus conocimientos y dar vida a tus aplicaciones React.

También puedes hacer que tu aplicación React funcione en directo desplegándola en el alojamiento de Aplicaciones de Kinsta ¡gratis!

Ahora es tu turno. ¿Qué opinas del Hook useEffect? No dudes en compartirlo con nosotros en la sección de comentarios.