Dominando useState y useEffect en React Hooks

Los React Hooks son una adición fundamental a la librería que permiten a los desarrolladores utilizar el estado y otras características de React sin escribir clases. Esta guía se centrará en dos de los Hooks más utilizados: useState y useEffect, explorando su sintaxis, funcionalidad y patrones de uso.

useState: Gestión del Estado en Componentes Funcionales

useState es el Hook que permite a los componentes funcionales tener estado. Se invoca dentro del cuerpo de un componente funcional y devuelve un array con dos elementos: el valor del estado actual y una función para actualizarlo.

Ejemplo Básico de useState

Consideremos un contador simple. Sin useState, un componente funcional no podría mantener un valor que cambie con las interacciones del usuario.

import React, { useState } from 'react';

function ContadorDeClics() {
  // 'contador' es el valor actual, 'setContador' es la función para actualizarlo.
  // El argumento inicial (0) es el valor por defecto del estado.
  const [contador, setContador] = useState(0);

  return (
    <div>
      <p>Has hecho clic {contador} veces</p>
      <button onClick={() => setContador(contador + 1)}>
        Incrementar
      </button>
    </div>
  );
}

Inicialización Diferida del Estado

El argumento inicial de useState puede ser un valor directo o una función. Cuando se proporciona una función, React la ejecutará solo una vez durante la renderización inicial del componente. Esto es útil para cálculos costosos o lógica de inicialización compleja, ya que evita re-ejecutarla en cada renderizado.

const [datosUsuario, setDatosUsuario] = useState(() => {
  // Esta función se ejecuta solo en la primera renderización.
  console.log('Cargando datos de usuario iniciales...');
  // Aquí se podría realizar una operación costosa o una lectura de localStorage.
  const valorInicial = { id: 1, nombre: 'Visitante' };
  return valorInicial;
});

Si el valor inicial es una constante simple, no hay diferencia perceptible en el rendimiento entre pasar el valor directamente o una función que lo retorne. Sin embargo, para valores computados o complejos, la función de inicialización diferida es la opción recomendada.

Actualizando el Estado con setEstado

La función setContador (o cualquier función de actualización de estado) solo acepta el nuevo valor del estado. A diferencia de this.setState en los componentes de clase, no ofrece un callback directo para ejecutar lógica después de que el estado se haya actualizado. Si necesitas realizar una acción después de una actualización de estado, useEffect es el Hook adecuado para ello.

useEffect: Ejecución de Efectos Secundarios

useEffect permite realizar efectos secundarios en componentes funcionales, como suscripciones, manipulación del DOM, llamadas a API, o la configuración de temporizadores. Se ejecuta después de cada renderizado del componente, a menos que se especifiquen dependencias.

Sintaxis y Uso Básico de useEffect

import React, { useState, useEffect } from 'react';

function MonitorDeAnchoVentana() {
  const [anchoVentana, setAnchoVentana] = useState(window.innerWidth);

  useEffect(() => {
    // Este código se ejecuta después de que el componente se renderiza.
    console.log('El ancho de la ventana ha sido detectado:', anchoVentana);

    const manejarRedimension = () => {
      setAnchoVentana(window.innerWidth);
    };

    window.addEventListener('resize', manejarRedimension);

    // Función de limpieza (opcional): se ejecuta antes de la próxima ejecución del efecto
    // o cuando el componente se desmonta (componentWillUnmount).
    return () => {
      console.log('Limpiando el listener de redimensionamiento...');
      window.removeEventListener('resize', manejarRedimension);
    };
  }, [anchoVentana]); // Dependencias: el efecto se re-ejecuta si 'anchoVentana' cambia

  return (
    <div>
      <p>Ancho actual de la ventana: {anchoVentana}px</p>
    </div>
  );
}

Controlando la Ejecución del Efecto con el Array de Dependencias

El segundo argumento de useEffect es un array de dependencias (deps). Este array controla cuándo se vuelve a ejecutar el efecto:

  • Sin array de dependencias: El efecto se ejecuta después de cada renderizado del componente.
  • Array vacío ([]): El efecto se ejecuta solo una vez después de la primera renderización (similar a componentDidMount) y la función de limpieza se ejecuta cuando el componente se desmonta (similar a componentWillUnmount).
  • Array con valores ([variable1, variable2]): El efecto se ejecuta solo cuando uno de los valores listados en el array cambia entre renderizados.

Simulando Callbacks de setEstado con useEffect

Como mencionamos, useState no tiene un callback directo. Sin embargo, useEffect puede utilizarse para lograr un comportamiento similar, ejecutando código solo después de que el estado se ha actualizado.

import React, { useState, useEffect } from 'react';

function GestorDeTareas() {
  const [tareasCompletadas, setTareasCompletadas] = useState(0);

  useEffect(() => {
    // Este efecto se dispara cada vez que 'tareasCompletadas' cambia.
    // Simula un callback post-actualización de estado.
    if (tareasCompletadas > 0) {
      console.log(`¡Excelente! Ahora tienes ${tareasCompletadas} tareas completadas.`);
      // Aquí podrías enviar una notificación o actualizar otra parte de la UI.
    }
  }, [tareasCompletadas]); // El efecto se re-ejecuta cuando 'tareasCompletadas' cambia.

  return (
    <div>
      <p>Tareas completadas: {tareasCompletadas}</p>
      <button onClick={() => setTareasCompletadas(prev => prev + 1)}>
        Marcar Tarea Completada
      </button>
    </div>
  );
}

Consideraciones sobre las Dependencias de useEffect

La forma en que useEffect detecta cambios en sus dependencias es crucial, especialmente con objetos y funciones. React realiza una comparación superficial (por referencia) de los valores en el array de dependencias.

  • Valores Primitivos (string, number, boolean, null, undefined): Los cambios se detectan correctamente ya que el valor en sí cambia.
  • Objetos y Funciones: El efecto se disparará solo si la referencia del objeto o función en el array de dependencias cambia.
    • Si tienes un objeto miObjeto = { a: 1 } en tus dependencias y luego modificas miObjeto.a = 2, el efecto no se disparará porque la referencia a miObjeto sigue siendo la misma.
    • Para que el efecto se dispare, debes crear una nueva referencia al objeto, por ejemplo, setMiObjeto({ ...miObjeto, a: 2 }) o setMiObjeto(nuevoObjeto).
    • Lo mismo aplica para las funciones. Si una función se redefine en cada renderizado (lo cual es común en componentes funcionales), se considerará una nueva referencia y disparará el efecto, a menos que se memoice con useCallback.

Comprender cómo React gestiona las dependencias es fundamental para evitar ejecuciones innecesarias de efectos o, por el contrario, que un efecto no se dispare cuando debería.

Etiquetas: React hooks useState useEffect JavaScript

Publicado el 7-19 20:00