Un efecto secundario es cualquier acción que ocurre dentro de tu aplicación que no está directamente relacionada con la representación de la interfaz de usuario. Por ejemplo:
setTimeout o setInterval.Estos efectos secundarios no modifican directamente la interfaz de usuario, lo que significa que React no vuelve a renderizar el componente en estos casos.
React ofrece hooks como useEffect, useLayoutEffect y useInsertionEffect para manejar efectos secundarios. El más comúnmente utilizado es useEffect.
useEffect vs useLayoutEffect: Diferencias Clave| Característica | useEffect | useLayoutEffect |
|---|---|---|
| Ejecución | Asincrónica tras el renderizado. | Sincrónica después de las mutaciones del DOM. |
| Momento de ejecución | Tras pintar el navegador. | Antes de que el navegador pinte. |
| Uso principal | Operaciones como solicitudes de API o subscripciones. | Mediciones y modificaciones del DOM. |
| Rendimiento | Mejor para efectos que no bloquean la pintura. | Puede bloquear la pintura si es costoso. |
useEffect?useEffect permite realizar efectos secundarios después de que el componente se haya renderizado.
El useEffect combina las funcionalidades de tres métodos del ciclo de vida de los componentes en React: cuando se crea el componente, se actualiza y se destruye. Esto permite replicar su comportamiento en componentes funcionales. Es ideal para tareas asincrónicas como:
useEffectimport { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Componente montado.');
// Cleanup opcional
return () => {
console.log('Componente desmontado.');
};
}, []); // Dependencias vacías, se ejecuta solo al montar.
return <div>Hola mundo</div>;
} import { useEffect, useState } from 'react';
function FetchData({ userId }) {
const [userData, setUserData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
setUserData(data);
};
fetchData();
}, [userId]);
return (
<div>
{userData ? (
<div>
<h1>{userData.name}</h1>
<p>{userData.email}</p>
</div>
) : (
'Cargando...'
)}
</div>
);
} useEffect?El principal inconveniente de useEffect es que se ejecuta de forma asincrónica y solo después de que el componente ha sido montado.
Esto significa que no es adecuado para manejar efectos secundarios que dependen del diseño del componente o del estado del DOM inmediatamente después del renderizado.
useLayoutEffect?useLayoutEffect se ejecuta de forma sincrónica después de las mutaciones del DOM, pero antes de que el navegador pinte la pantalla.
El hook useLayoutEffect se ejecuta de forma sincrónica, inmediatamente después de que React haya realizado las mutaciones necesarias en el DOM, pero antes de que el navegador pinte la pantalla.
Comparte la misma API y sintaxis que useEffect.
Este hook se creó para abordar problemas específicos relacionados con efectos secundarios que requieren acceso inmediato al DOM actualizado, lo que useEffect no puede garantizar debido a su naturaleza asincrónica. Esto lo hace adecuado para:
useLayoutEffectimport { useLayoutEffect } from 'react';
function MyComponent() {
useLayoutEffect(() => {
console.log('Se ejecuta antes del pintado del navegador.');
}, []);
return <div>Hola mundo</div>;
} import { useLayoutEffect, useRef, useState } from 'react';
function MeasureDiv() {
const divRef = useRef(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useLayoutEffect(() => {
const { width, height } = divRef.current.getBoundingClientRect();
setDimensions({ width, height });
}, []);
return (
<div>
<div ref={divRef} style={{ width: '200px', height: '100px', background: 'lightblue' }}>
Mídeme
</div>
<p>Dimensiones: {dimensions.width}x{dimensions.height}</p>
</div>
);
} useEffectimport { useEffect, useState } from 'react';
function AnimationEffect() {
const [opacity, setOpacity] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setOpacity((prev) => (prev < 1 ? prev + 0.1 : 0));
}, 100);
return () => clearInterval(interval);
}, []);
return <div style={{ opacity, background: 'blue', width: '100px', height: '100px' }} />;
} import { useEffect } from 'react';
function GlobalEventHandler() {
useEffect(() => {
const handleResize = () => console.log('Window resized');
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <div>Redimensiona la ventana para ver el efecto.</div>;
} useEffectimport { useEffect, useState } from 'react';
function LoadingSimulation() {
const [loading, setLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setLoading(false), 2000);
return () => clearTimeout(timer);
}, []);
return <div>{loading ? 'Cargando...' : 'Carga completa'}</div>;
} useLayoutEffectimport { useLayoutEffect, useRef } from 'react';
function ScrollToTop() {
const divRef = useRef(null);
useLayoutEffect(() => {
divRef.current.scrollTop = 0;
}, []);
return (
<div ref={divRef} style={{ overflow: 'auto', height: '200px' }}>
{Array(50)
.fill('Texto de prueba')
.map((text, index) => (
<p key={index}>{text}</p>
))}
</div>
);
} useEffectimport { useEffect, useState } from 'react';
function SyncData({ data }) {
const [syncedData, setSyncedData] = useState(null);
useEffect(() => {
setSyncedData(data);
}, [data]);
return <div>Datos sincronizados: {syncedData}</div>;
} useEffect para operaciones que no necesitan bloquear la pintura del navegador.useLayoutEffect para tareas que dependen de la medición o manipulación del DOM antes del pintado.