Efectos
Imagina un componente que necesita mantener un temporizador marcando después de renderizarse, como hace el componente Timer de abajo. La mayoría de lo que hace un componente sucede mientras se renderiza: lee props y estado y devuelve JSX. Mantener un temporizador en marcha es diferente. También lo es abrir una suscripción, sincronizar el título del documento, o traer datos desde un servidor. Cada uno de esos casos llega hacia afuera para tocar algo que React no controla. useEffect es el hook para eso. Te permite ejecutar código en un componente después de renderizarse, para que pueda sincronizarse con esa cosa del mundo exterior.
Aquí hay un efecto que inicia el temporizador una sola vez e incrementa un contador cada segundo:
import { useState, useEffect } from 'react'
function Timer() {
const [count, setCount] = useState(0)
useEffect(() => {
const id = setInterval(() => setCount(c => c + 1), 1000)
return () => clearInterval(id)
}, [])
return <p>{count}</p>
}Tres partes están haciendo el trabajo aquí, y se corresponden con las tres cosas con las que todo efecto lidia: qué hacer, cómo limpiarlo, y cuándo ejecutarlo.
La función que pasas a useEffect es el efecto mismo. Esta llama a setInterval para iniciar un temporizador repetido que aumenta count en uno cada segundo.
El array de dependencias
El segundo argumento, el [] al final, es el array de dependencias. Controla cuándo se ejecuta el efecto.
- Un array vacío
[]significa que el efecto se ejecuta una vez, después de que el componente aparezca en la pantalla por primera vez. El temporizador de arriba usa esto: inicia el intervalo una sola vez y déjalo corriendo. - Un array con valores, como
[a, b], significa que el efecto se ejecuta después del primer render y de nuevo cada vez que uno de esos valores cambia entre renders. Así es como resincronizas cuando algo de lo que depende el efecto ha cambiado. - Omitir el array por completo significa que el efecto se ejecuta después de cada render. Eso rara vez es lo que quieres, y es una fuente común de bucles descontrolados cuando el efecto también actualiza estado.
El array es tu respuesta a la pregunta "¿cuándo debería este código ejecutarse de nuevo?" Lista los valores que el efecto lee y de los que depende, y React lo ejecuta de nuevo cuando cualquiera de ellos cambia.
Funciones de limpieza
El efecto de arriba devuelve una función:
return () => clearInterval(id)Esa función devuelta es la función de limpieza. React la ejecuta antes de que el efecto se ejecute de nuevo, y una vez más cuando el componente se quita de la pantalla. Su trabajo es deshacer lo que configuró el efecto.
La limpieza importa porque los efectos frecuentemente inician algo que sigue andando por su cuenta: un intervalo, un event listener, una conexión abierta. Si el componente se quita y nada detiene ese intervalo, sigue disparándose para siempre y mantiene una referencia a un componente que ya no existe. Hazlo algunas veces y tienes una fuga lenta con actualizaciones llegando a cosas que ya no existen. La regla de oro: si un efecto inicia algo, se suscribe a algo, o abre algo, su función de limpieza debe detenerlo, desuscribirse, o cerrarlo.
Es posible que no necesites un efecto
Los efectos son para sincronizar con el mundo exterior, así que mucho código que se ve como candidato para useEffect pertenece a otro lugar. Dos casos surgen constantemente.
El primero es valores derivados. Si puedes calcular algo de props y estado que ya tienes, calcúlalo justo durante el render:
function Cart({ items }) {
const total = items.reduce((sum, item) => sum + item.price, 0)
return <p>Total: {total}</p>
}total se deriva de items, así que se calcula en cada render y siempre está sincronizado. Almacenarlo en su propio estado y actualizarlo desde un efecto sería más código y un render extra sin ganancia.
El segundo es responder a una acción del usuario. Cuando algo debe suceder porque el usuario hizo clic o escribió, ese código va en el manejador de eventos para esa acción:
function BuyButton({ product }) {
function handleClick() {
buyProduct(product)
}
return <button onClick={handleClick}>Buy</button>
}La compra sucede en handleClick, justo donde se maneja el clic. Enrutarlo a través de un efecto añadiría una capa de indirección y haría el flujo más difícil de seguir. Una buena prueba: si el código se ejecuta por una interacción específica, usa un manejador de eventos; si se ejecuta para mantener el componente sincronizado con un sistema externo, usa un efecto.
Nota de versión
En class components este comportamiento se dividía entre tres métodos del ciclo de vida: componentDidMount para configuración después del primer render, componentDidUpdate para ejecutarse de nuevo cuando los datos cambiaban, y componentWillUnmount para limpieza. Un único useEffect con un array de dependencias y una función de limpieza cubre los tres, por eso el código relacionado de configuración y desmontaje ahora vive junto en lugar de esparcirse entre métodos separados.
useEffect como el lugar para código que llega fuera de React: un temporizador, el título del documento, una solicitud a un servidor. El array al final dice cuándo ejecutarlo, [] significando una vez, justo después de que el componente aparezca. Y si tu efecto inicia algo, devuelve una pequeña función que lo detenga. La mayoría del tiempo no necesitarás un efecto en absoluto, así que es un buen hábito pausar y preguntar si un cálculo simple o un manejador de clic haría el trabajo primero. Siguiente: Traer datos, donde los componentes traen datos que viven completamente fuera de React.

