Los Hooks son funciones especiales que te permiten “engancharte” a características de React desde componentes funcionales. Fueron introducidos en React 16.8 para permitir usar estado y otras características de React sin escribir componentes de clase.
useEffectEl hook useEffect en React permite ejecutar efectos secundarios en componentes funcionales, como llamadas a APIs, suscripciones, manipulaciones del DOM, o cualquier operación que no se pueda hacer directamente durante el renderizado.
Se ejecuta después de que el componente se renderiza.
import { useEffect } from 'react';
function ComponenteConEfecto() {
// Ejecución inicial del componente
useEffect(() => {
// Código que quieres ejecutar cuando el componente se monta
console.log('Componente montado');
// Retorno opcional: función de limpieza que se ejecuta cuando el componente se desmonta
return () => {
console.log('Componente desmontado');
};
}, [/* dependencias */]); // Dependencias vacías: solo se ejecuta una vez al montar y desmontar El hook useEffect en React permite ejecutar efectos secundarios en componentes funcionales, como actualizaciones de datos o suscripciones a eventos. A continuación, te explicamos sus principales elementos:
Función de efecto: El primer argumento de useEffect es una función que se ejecuta después de cada renderizado del componente. Aquí puedes realizar acciones como llamadas a API, suscripciones o cambios en el DOM.
Función de limpieza: Al retornar una función dentro de useEffect, defines una acción de limpieza que se ejecutará antes de desmontar el componente o antes de que el efecto se vuelva a ejecutar. Esto es útil para liberar recursos, como detener suscripciones o limpiar temporizadores.
Array de dependencias: El segundo argumento es un array de dependencias que indica cuándo debe ejecutarse el efecto. Si el array está vacío ([]), el efecto se ejecutará solo una vez al montar el componente y no se volverá a ejecutar en renderizados futuros. Si incluye variables o estados, el efecto se ejecutará cada vez que alguno de ellos cambie.
import React, { useState, useEffect } from 'react';
function EjemploUseEffect() {
const [contador, setContador] = useState(0);
useEffect(() => {
document.title = `Has clickeado ${contador} veces`;
}, [contador]); // Se ejecuta cada vez que el contador cambia
return (
<div>
<p>Has clickeado {contador} veces</p>
<button onClick={() => setContador(contador + 1)}>Clic aquí</button>
</div>
);
} useEffect actualiza el título de la página cada vez que cambia el contador.[contador] como dependencia, el efecto se ejecuta solo cuando contador cambia. Si se pasa un array vacío ([]), solo se ejecutaría una vez, al montarse el componente.useEffect con Dependencia VacíaEjecución Solo al Montar el Componente. Este ejemplo muestra cómo usar useEffect con un array vacío ([]) para ejecutar un efecto solo una vez, cuando el componente se monta, ideal para inicializaciones o llamadas a APIs.
import React, { useEffect } from 'react';
function Componente() {
useEffect(() => {
console.log("Este efecto se ejecuta solo una vez, al montarse el componente");
// Aquí puedes poner código que solo se ejecute una vez, como una llamada a una API
}, []); // El array vacío significa que solo se ejecuta una vez
return (
<div>
<p>Componente montado</p>
</div>
);
} useEffect se ejecuta solo una vez, justo después de que el componente se haya montado.[] como segundo argumento indica que no hay dependencias, por lo que el efecto solo se ejecuta una vez cuando el componente se monta por primera vez.useEffect sin Array de DependenciasSi no proporcionas un array de dependencias en useEffect, el efecto se ejecutará después de cada renderizado del componente, lo que incluye la primera vez que el componente se monta. Esto puede ser útil cuando deseas ejecutar un efecto cada vez que el componente se renderiza, sin depender de ningún valor específico.
import { useEffect, useState } from 'react';
function ComponenteSinDependencias() {
const [contador, setContador] = useState(0);
// Este useEffect se ejecuta después de cada renderizado
useEffect(() => {
console.log('El componente ha sido renderizado o actualizado');
// Función de limpieza (opcional) que se ejecutará antes de que el efecto se ejecute nuevamente o cuando el componente se desmonte
return () => {
console.log('Limpiando efecto');
};
}); // Sin array de dependencias: se ejecuta en cada renderizado
const incrementarContador = () => {
setContador(contador + 1);
};
return (
<div>
<p>Contador: {contador}</p>
<button onClick={incrementarContador}>Incrementar</button>
</div>
);
}
export default ComponenteSinDependencias; []), useEffect se ejecuta siempre que el componente se renderiza. Esto incluye tanto la primera ejecución al montar el componente como cualquier actualización posterior del componente.return dentro de useEffect), se ejecutará antes de la siguiente ejecución del efecto o cuando el componente se desmonte.useEffect de nuevo.Este enfoque se utiliza cuando necesitas realizar alguna acción cada vez que el componente se renderiza, sin importar qué valores hayan cambiado.
useEffect sin Array de DependenciasEn React, al usar useEffect sin dependencias, el efecto se ejecuta después de cada renderizado.
Esto es útil para realizar acciones en cada actualización del componente, como manejar suscripciones o temporizadores.
También puedes incluir una función de limpieza para ejecutar antes de la siguiente ejecución del efecto o cuando el componente se desmonte.
import React, { useEffect, useState } from 'react';
function ComponenteSinDependencias() {
const [contador, setContador] = useState(0);
// Este useEffect se ejecuta después de cada renderizado
useEffect(() => {
console.log('El componente ha sido renderizado o actualizado');
// Función de limpieza (opcional) que se ejecutará antes de que el efecto se ejecute nuevamente o cuando el componente se desmonte
return () => {
console.log('Limpiando efecto');
};
}); // Sin array de dependencias: se ejecuta en cada renderizado
const incrementarContador = () => {
setContador(contador + 1);
};
return (
<div>
<p>Contador: {contador}</p>
<button onClick={incrementarContador}>Incrementar</button>
</div>
);
}
export default ComponenteSinDependencias; useEffectDentro del useEffect puede definirse una función asíncrona (por ejemplo, para consultar una API) y llamarla inmediatamente al cargar el componente.
Sin embargo, como useEffect no puede ser directamente asíncrono, se recomienda definir la función asíncrona dentro de él y luego invocarla.
import React, { useState, useEffect } from 'react';
function Componente() {
const [datos, setDatos] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Definir una función asíncrona dentro de useEffect
const fetchDatos = async () => {
try {
const response = await fetch('https://api.example.com/datos');
const data = await response.json();
setDatos(data);
} catch (error) {
console.error('Error al obtener los datos:', error);
} finally {
setLoading(false); // Marcar como cargado una vez se termine
}
};
fetchDatos(); // Llamar a la función asíncrona
}, []); // El array vacío asegura que solo se ejecute una vez
return (
<div>
<h2>Datos de la API</h2>
{loading ? (
<p>Cargando...</p>
) : (
<pre>{JSON.stringify(datos, null, 2)}</pre>
)}
</div>
);
}
export default Componente; fetchDatos: Dentro de useEffect, se define una función asíncrona fetchDatos que consulta la API y maneja la respuesta.fetch para obtener los datos de la API y luego se actualiza el estado con los datos obtenidos.loading para mostrar un mensaje de “Cargando…” mientras los datos se están obteniendo, y después se muestra la respuesta.useEffect con DependenciasCuando usas dependencias en useEffect, el efecto se ejecuta cada vez que las dependencias cambian.
Si pasas variables dentro del array de dependencias, React ejecutará el efecto nuevamente siempre que esas variables cambien.
Aquí tienes un ejemplo donde se consulta una API cada vez que cambia el valor de una variable query (por ejemplo, una palabra clave de búsqueda)
import React, { useState, useEffect } from 'react';
function Componente() {
const [query, setQuery] = useState('react');
const [datos, setDatos] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchDatos = async () => {
setLoading(true);
try {
const response = await fetch(`https://api.example.com/search?q=${query}`);
const data = await response.json();
setDatos(data);
} catch (error) {
console.error('Error al obtener los datos:', error);
} finally {
setLoading(false);
}
};
fetchDatos(); // Llamar a la función asíncrona
}, [query]); // El efecto se ejecuta cada vez que cambia 'query'
return (
<div>
<h2>Buscar Datos</h2>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)} // Actualizar la query
placeholder="Buscar..."
/>
{loading ? (
<p>Cargando...</p>
) : (
<pre>{JSON.stringify(datos, null, 2)}</pre>
)}
</div>
);
}
export default Componente; query: El useEffect ahora depende de la variable query. Cada vez que cambie el valor de query (como resultado de escribir en el input), se ejecutará nuevamente la función que consulta la API.query, por lo que el efecto se ejecuta con la nueva consulta cada vez que el valor de query cambia.query: Cuando el usuario escribe en el campo de texto, se actualiza query, lo que dispara una nueva ejecución del efecto.useEffectEn React, puedes tener múltiples useEffect dentro de un mismo componente, cada uno con diferentes dependencias.
Los useEffect se ejecutarán de forma independiente según sus propias dependencias.
import React, { useState, useEffect } from 'react';
function Componente() {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('');
// Efecto 1: Imprimir el valor de "count" cuando cambia
useEffect(() => {
console.log('El valor de count ha cambiado:', count);
}, [count]); // Se ejecuta cuando "count" cambia
// Efecto 2: Establecer un mensaje después de 2 segundos
useEffect(() => {
const timer = setTimeout(() => {
setMessage('¡Han pasado 2 segundos!');
}, 2000);
// Limpiar el timeout al desmontar el componente
return () => clearTimeout(timer);
}, []); // Este efecto solo se ejecuta una vez cuando el componente se monta
return (
<div>
<h2>Contador: {count}</h2>
<button onClick={() => setCount(count + 1)}>Aumentar contador</button>
<p>{message}</p>
</div>
);
}
export default Componente; count cada vez que cambia. Esto ocurre siempre que presionas el botón para aumentar el contador.useEffect solo se ejecuta una vez cuando el componente se carga por primera vez.Este ejemplo es más sencillo y muestra cómo usar múltiples efectos para realizar acciones diferentes, como observar cambios en el estado y ejecutar código en el montaje del componente.
useEffect en ReactEn este contexto, useEffect depende de un objeto y se activa cada vez que alguna de sus propiedades cambia, mostrando cómo reaccionar a las actualizaciones de un objeto en el estado.
import { useEffect, useState } from 'react';
function ComponenteConObjeto() {
const [usuario, setUsuario] = useState({
nombre: 'Urian Viera',
edad: 35
});
// useEffect que depende del objeto "usuario"
useEffect(() => {
console.log(`Nombre del usuario: ${usuario.nombre}`);
console.log(`Edad del usuario: ${usuario.edad}`);
// Efecto de limpieza opcional
return () => {
console.log('Limpiando efecto del usuario...');
};
}, [usuario]); // Se ejecuta cada vez que cambia alguna propiedad del objeto "usuario"
// Cambia los datos del usuario al hacer clic
const actualizarUsuario = () => {
setUsuario((prevUsuario) => ({
...prevUsuario,
edad: prevUsuario.edad + 1,
}));
};
return (
<div>
<p>Nombre: {usuario.nombre}</p>
<p>Edad: {usuario.edad}</p>
<button onClick={actualizarUsuario}>Aumentar Edad</button>
</div>
);
}
export default ComponenteConObjeto; [usuario] como dependencia, el useEffect se ejecutará cada vez que haya un cambio en el objeto usuario, como cuando cambia nombre o edad.useEffect.actualizarUsuario usa setUsuario para modificar solo la propiedad edad del objeto, conservando el resto del objeto usuario con el operador spread (...prevUsuario).null o undefined con useEffectCuando una dependencia en useEffect es null o undefined, el efecto puede comportarse de las siguientes maneras:
useEffect aún se ejecuta al inicio y luego cada vez que el valor de la dependencia cambia, incluso si cambia a null o undefined.undefined a null (o viceversa), React considera esto como un cambio, por lo que el efecto se ejecutará nuevamente.Aquí tienes un ejemplo sencillo para ver cómo null o undefined como dependencia puede activar el useEffect:
import { useEffect, useState } from 'react';
function ComponenteConDependenciaNula() {
const [data, setData] = useState(null);
// Este useEffect depende de "data", y se ejecutará cada vez que "data" cambie
useEffect(() => {
if (data === null) {
console.log('Data está en null');
} else if (data === undefined) {
console.log('Data está en undefined');
} else {
console.log(`Data tiene valor: ${data}`);
}
}, [data]); // El efecto se ejecuta cada vez que "data" cambia, incluso si cambia a null o undefined
// Simula cambios de estado
const establecerData = (valor) => setData(valor);
return (
<div>
<p>{data !== null && data !== undefined ? `Data: ${data}` : 'Data es null o undefined'}</p>
<button onClick={() => establecerData('Nuevo valor')}>Establecer Data</button>
<button onClick={() => establecerData(null)}>Establecer null</button>
<button onClick={() => establecerData(undefined)}>Establecer undefined</button>
</div>
);
}
export default ComponenteConDependenciaNula; null: Al hacer clic en el botón “Establecer null”, data cambia a null, lo que activa el useEffect y muestra Data está en null.undefined: Al hacer clic en el botón “Establecer undefined”, data cambia a undefined, activando nuevamente el useEffect y mostrando Data está en undefined.distinto: Cualquier cambio de data activa el useEffect, ya sea a null, undefined o cualquier otro valor, siempre que sea diferente al valor anterior.Este manejo es útil para condiciones en las que necesitas saber si un valor está null o undefined para tomar acciones específicas dentro del useEffect.
useEffect en React// Mala práctica:
// Ejecutar el efecto en cada renderizado sin tener un array de dependencias adecuado.
useEffect(() => {
console.log("Este efecto se ejecuta después de cada renderizado");
});
// Buena práctica:
// Definir correctamente las dependencias para evitar ejecuciones innecesarias.
useEffect(() => {
console.log("Este efecto se ejecuta solo cuando `variable` cambia");
}, [variable]); // Solo se ejecuta cuando `variable` cambia // Mala práctica:
// Olvidar agregar dependencias a un array vacío o no incluir todas las dependencias necesarias.
useEffect(() => {
console.log("Efecto con dependencias incompletas");
// Se usa `count` pero no está en las dependencias
}, []);
// Buena práctica:
// Asegúrate de incluir todas las variables usadas dentro del efecto en el array de dependencias.
useEffect(() => {
console.log("Efecto con dependencias completas");
}, [count]); // Se incluye `count` para que el efecto se ejecute cuando cambie // Mala práctica:
// Modificar el estado dentro de un efecto sin control, lo que puede causar ciclos infinitos de renderizados.
useEffect(() => {
setCount(count + 1); // Esto provoca un ciclo infinito
}, [count]);
// Buena práctica:
// Usar funciones de actualización de estado que eviten ciclos infinitos, usando el valor más reciente del estado.
useEffect(() => {
setCount(prevCount => prevCount + 1); // Evita ciclos infinitos
}, [count]); // Mala práctica:
// No limpiar suscripciones, temporizadores u otros recursos al desmontar el componente.
useEffect(() => {
const timer = setInterval(() => console.log('tick'), 1000);
// No se limpia el intervalo
}, []);
// Buena práctica:
// Limpiar los recursos en la función de limpieza para evitar fugas de memoria.
useEffect(() => {
const timer = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(timer); // Limpiar el intervalo
}, []); // Mala práctica:
// No manejar adecuadamente dependencias de objetos o arrays en useEffect, lo que puede provocar ejecuciones innecesarias.
const settings = { theme: 'dark' };
useEffect(() => {
console.log(settings.theme); // Se ejecutará en cada renderizado
}, [settings]); // Los objetos/arrays son referenciados por su identidad
// Buena práctica:
// Usar variables primitivas o técnicas de memoización (como useMemo o useCallback) para evitar cambios de referencia innecesarios.
const settings = useMemo(() => ({ theme: 'dark' }), []);
useEffect(() => {
console.log(settings.theme); // Se ejecuta solo cuando `settings` cambia
}, [settings]); // Mala práctica:
// Usar un array vacío sin entender bien su funcionamiento, lo que puede llevar a efectos que se ejecutan solo una vez pero con dependencias ocultas.
useEffect(() => {
// Ejecutar una vez al montar el componente
}, []); // Peligroso si el efecto depende de alguna variable interna
// Buena práctica:
// Siempre revisar que el efecto no dependa de ningún valor que deba estar en las dependencias.
useEffect(() => {
// Ejecutar código al montar, pero asegúrate de que no dependa de ningún valor mutable
}, []); // Solo si el efecto no depende de ningún valor mutable // Mala práctica:
// Utilizar useEffect para ejecutar lógica que podría hacerse de manera más sencilla sin un efecto.
useEffect(() => {
if (count > 10) {
console.log('Contador mayor a 10');
}
}, [count]); // Usar useEffect aquí es innecesario
// Buena práctica:
// Simplemente usar la lógica directamente en el cuerpo del componente.
if (count > 10) {
console.log('Contador mayor a 10');
} async/await directamente dentro de useEffect// Mala práctica:
// Usar async/await directamente dentro de useEffect, lo que puede causar advertencias o un comportamiento inesperado.
useEffect(async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}, []);
// Buena práctica:
// Usar una función asíncrona dentro del useEffect de manera separada, para manejar correctamente las operaciones asincrónicas.
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
};
fetchData();
}, []); useEffect para prevenir ciclos infinitos.async/await directamente en useEffect. En su lugar, define funciones asíncronas dentro del efecto para evitar errores.