Efectos secundarios en React

useEffect y los side-effects en React


En React, los efectos secundarios se gestionan mediante el hook useEffect, pero antes de empezar a trabajar con ellos necesitamos entender sus bases. En programación, llamamos efecto secundario (o side-effect) a cualquier acción que realiza nuestro código (en el interior de una función, por ejemplo) y afecta a algo que se encuentra fuera del ámbito de esa función, es decir, que estamos alterando algo no relacionado directamente con lo que devolvemos en la función.

Efectos secundarios

Para entender mejor el concepto de efecto secundario de la programación, imaginemos esta sencilla función de un contador:

let counter = 0;

function incrementCounter() {
  counter++; // Modifica la variable global (efecto secundario)
}

incrementCounter();
console.log(counter); // 1

Dicha función está alterando el valor de counter, el cuál no pertenece a la función incrementCounter(), por lo que aunque está realizando nuestro objetivo correctamente (incrementar el contador), se está realizando mediante un efecto secundario.

Algunos ejemplos comunes de efectos secundarios en JavaScript podrían ser:

  • Modificar variables fuera del ámbito de su función (ej: variables globales)
  • Realizar una operación de entrada/salida (ej: escribir en un archivo, petición a una API...)
  • Actualizar y modificar elementos del DOM (ej: cambios en la estructura de la página)
  • Crear temporizadores (ej: setTimeout o setInterval)
  • Suscribirse a eventos (ej: escuchar un click u otra acción)

Funciones puras

El ejemplo anterior se puede reeescribir intentando que tus funciones sean puras. Una función pura es una función que cumple estas condiciones:

  • 1️⃣ Dado un argumento por parámetro, siempre devuelve el mismo resultado (es determinista).
  • 2️⃣ No muta nada fuera de su ámbito (no tiene efectos secundarios).

Veamos el ejemplo anterior, cambiando la función incrementCounter() para que sea una función pura, y evitando la creación de efectos secundarios:

let counter = 0;

function incrementCounter(counter) {
  return counter + 1;
}

counter = incrementCounter(counter);
console.log(counter); // 1

Observa que en lugar de mutar la variable externa counter en la función, lo que hacemos es devolver una versión modificada de los datos que recibimos. Esto ayuda considerablemente a no crear efectos secundarios y a que nuestro código sea más predecible y evitemos bugs accidentales.

Side effects en React

En el ecosistema de React, el concepto efecto secundario (muchas veces abreviado como efecto) aparece mucho y también se suele relacionar con las acciones que tienen consecuencias fuera de la función o componente en la que se ejecuta.

En ReactLand™ se tiende a evitar los efectos secundarios en las funciones o componentes siempre que sea posible, ya que no encajan bien con la filosofía de React:

  • 1️⃣ React renderiza la UI cuando cambia el estado (predecible). Los efectos no son predecibles.
  • 2️⃣ Efectos innecesarios pueden impactar negativamente en el rendimiento.
  • 3️⃣ Los efectos secundarios descontrolados son dificiles de depurar y testear.

Sin embargo, a pesar de estas dificultades, los efectos secundarios son a menudo inevitables para construir una aplicación. Para ello, existe el hook useEffect, una herramienta para manejarlos.

El hook useEffect

En React, existe un hook llamado useEffect que sirve para gestionar efectos secundarios de forma controlada, predecible y eficiente. Primero, analicemos la estructura de un useEffect() para conocer las partes clave de este hook:

useEffect(() => {
  /* Montaje */

  return () => { /* Desmontaje */ }
}, [/* dependencias */]);
  • 1️⃣ El useEffect() tiene dos parámetros: la función (de montaje) y las dependencias.
  • 2️⃣ La función del useEffect se ejecuta cuando se monta el componente
  • 3️⃣ El array deps indica las dependencias del useEffect
  • 4️⃣ La función devuelta por el return se ejecuta cuando se desmonta el componente

La parte más importante aquí probablemente sea el array de dependencias, así que vamos a explicarlo primero. Las situaciones son las siguientes:

DependenciasDescripciónMontajeEn cada renderizado
[]Sólo se ejecuta la primera vez (montaje).
[dep1]Se ejecuta primera vez y al cambiar.🟨 Cuando cambia dep1
[dep1, dep2]Se ejecuta primera vez y al cambiar.🟨 Cuando cambian dep1 y/o dep2
Sin array de dependenciasSe ejecuta primera vez y en cada renderizado.✅ Siempre

Con esto claro, veamos ahora un código real utilizando un useEffect. Vamos a crear un temporizador que se ejecutará cada segundo para mostrar la hora actual.

Al inicio del componente Clock creamos un estado time con la hora actual. Luego, creamos un useEffect con un array de dependencias vacío, por lo que se ejecutará solo la primera vez que se monta el componente:

import { useState, useEffect } from "react";

export function Clock() {
  const [time, setTime] = useState(new Date().toLocaleTimeString());

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(new Date().toLocaleTimeString());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <h1>Hora actual: {time}</h1>;
}

En dicho useEffect creamos el setInterval cada 1 segundo (1000ms) y mutamos el estado time para cambiar la hora actual.

Por último, en el return devolvemos una función que se ejecutará cuando se desmonte el componente, donde es necesario realizar tareas de limpieza para evitar fugas de memoria o problemas similares.

De esta forma puedes gestionar los efectos secundarios en tus componentes de React de forma controlada. Aunque es un sistema muy potente, ten mucho cuidado y no abuses de useEffect ya que puede complicar la lógica, impactar en el rendimiento y muchas veces se puede buscar una alternativa sin efectos secundarios.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev