diff --git a/beta/src/content/apis/react/useEffect.md b/beta/src/content/apis/react/useEffect.md index 547e3e87c..4eb195def 100644 --- a/beta/src/content/apis/react/useEffect.md +++ b/beta/src/content/apis/react/useEffect.md @@ -4,7 +4,7 @@ title: useEffect -`useEffect` is a React Hook that lets you [synchronize a component with an external system.](/learn/synchronizing-with-effects) +`useEffect` es un Hook de React que te permite [sincronizar un componente con un sistema externo.](/learn/synchronizing-with-effects) ```js useEffect(setup, dependencies?) @@ -16,13 +16,67 @@ useEffect(setup, dependencies?) --- -## Usage {/*usage*/} +## Referencia {/*reference*/} -### Connecting to an external system {/*connecting-to-an-external-system*/} +### `useEffect(configuración, dependencias?)` {/*useeffect*/} -Sometimes, your component might need to stay connected to the network, some browser API, or a third-party library, while it is displayed on the page. Such systems aren't controlled by React, so they are called *external.* +Declara un efecto con `useEffect` en el nivel superior de tu componente: -To [connect your component to some external system,](/learn/synchronizing-with-effects) call `useEffect` at the top level of your component: +```js +import { useEffect } from 'react'; +import { createConnection } from './chat.js'; + +function ChatRoom({ roomId }) { + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); + + useEffect(() => { + const connection = createConnection(serverUrl, roomId); + connection.connect(); + return () => { + connection.disconnect(); + }; + }, [serverUrl, roomId]); + // ... +} +``` + +[Ver más ejemplos.](#usage) + +#### Parámetros {/*parameters*/} + +* `configuración`: La función con la lógica de tu Efecto. Tu función de configuración también puede devolver opcionalmente una función de limpieza. Cuando tu componente se añade por primera vez al DOM, React ejecutará tu función de configuración. Después de cada renderización con dependencias cambiadas, React ejecutará primero la función de limpieza (si la proporcionaste) con los valores antiguos, y luego ejecutará tu función de configuración con los nuevos valores. Después de que tu componente sea eliminado del DOM, React ejecutará tu función de limpieza una última vez. + +* `dependencias` **opcionales**: La lista de todos los valores reactivos referenciados dentro del código de `configuración`. Los valores reactivos incluyen props, estados, y todas las variables y funciones declaradas directamente dentro del cuerpo de tu componente. Si tu linter está [configurado para React](/learn/editor-setup#linting), verificará que cada valor reactivo esté correctamente especificado como una dependencia. La lista de dependencias debe tener un número constante de elementos y estar escrita en línea como `[dep1, dep2, dep3]`. React comparará cada dependencia con su valor anterior utilizando el algoritmo de comparación [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Si no se especifican las dependencias en absoluto, su efecto se volverá a ejecutar después de cada renderización del componente. [ Mira la diferencia entre pasar un _array_ de dependencias, un _array_ vacío y ninguna dependencia.](#examples-dependencies) + +#### Retorno {/*returns*/} + +`useEffect` retorna `undefined`. + +#### Advertencias {/*caveats*/} + +* `useEffect` es un Hook, por lo que sólo puedes llamarlo en **el nivel superior de tu componente** o en tus propios Hooks. No puedes llamarlo dentro de bucles o condiciones. Si lo necesitas, extrae un nuevo componente y mueve el estado a él. + +* Si **no estás tratando de sincronizar con algún sistema externo,** [probablemente no necesites un Efecto.](/learn/you-might-not-need-an-effect) + +* Cuando el modo estricto está activado, React en el modo desarrollo **ejecutará un ciclo extra de configuración y limpieza** antes de la primera configuración real. Esta es una prueba de estrés que asegura que tu lógica de limpieza "refleje" tu lógica de configuración y que detenga o deshaga cualquier cosa que la configuración esté haciendo. Si esto causa un problema, [necesitas implementar la función de limpieza.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) + +* Si alguna de tus dependencias son objetos o funciones definidas dentro del componente, existe el riesgo de que **provoquen que el Efecto se re-ejecute más veces de las necesarias.** Para solucionar esto, elimina las dependencias innecesarias de [objetos](#removing-unnecessary-object-dependencies) y [funciones](#removing-unnecessary-function-dependencies). También puedes [extraer las actualizaciones de estados](#updating-state-based-on-previous-state-from-an-effect) y la [lógica no reactiva](#reading-the-latest-props-and-state-from-an-effect) fuera de tu Efecto. + +* Si tu efecto no ha sido causado por una interacción (como un clic), React dejará que el navegador **pinte primero la pantalla actualizada antes de ejecutar tu efecto.** Si tu efecto está haciendo algo visual (por ejemplo, posicionar un tooltip), y el retraso es notable (por ejemplo, parpadea), tendrás que reemplazar `useEffect` por [`useLayoutEffect`.](/reference/react/useLayoutEffect) + +* Incluso si tu Efecto fue causado por una interacción (como un clic), el navegador podría **volver a pintar la pantalla antes de procesar las actualizaciones de estado dentro de tu Efecto.** Normalmente, eso es lo que quieres. Sin embargo, si debes impedir que el navegador pinte de nuevo la pantalla, tendrás que reemplazar `useEffect` por [`useLayoutEffect`.](/reference/react/useLayoutEffect) + +* Los efectos **sólo se ejecutan en el lado del cliente.** No se ejecutan durante el renderizado del lado del servidor. + +--- + +## Uso {/*usage*/} + +### Conexión a un sistema externo {/*connecting-to-an-external-system*/} + +A veces, un componente puede necesitar permanecer conectado a la red, a alguna API del navegador, o a una librería de terceros, mientras se muestra en la página. Estos sistemas no están controlados por React, por lo que se denominan *externos.* + +Para [conectar tu componente a algún sistema externo,](/learn/synchronizing-with-effects) declara `useEffect` en el nivel superior de tu componente: ```js [[1, 8, "const connection = createConnection(serverUrl, roomId);"], [1, 9, "connection.connect();"], [2, 11, "connection.disconnect();"], [3, 13, "[serverUrl, roomId]"]] import { useEffect } from 'react'; @@ -42,45 +96,45 @@ function ChatRoom({ roomId }) { } ``` -You need to pass two arguments to `useEffect`: +Tienes que pasar dos argumentos a `useEffect`: -1. A *setup function* with setup code that connects to that system. - - It should return a *cleanup function* with cleanup code that disconnects from that system. -2. A list of dependencies including every value from your component used inside of those functions. +1. Una *función de configuración* con código de configuración que se conecta a ese sistema. + - La cual debería retornar una *función de limpieza* con código de limpieza que se desconecta de ese sistema. +2. Una lista de dependencias incluyendo cada valor de tu componente utilizado dentro de esas funciones. -**React calls your setup and cleanup functions whenever it's necessary, which may happen multiple times:** +**React llama a tus funciones de configuración y limpieza siempre que sea necesario, lo que puede ocurrir varias veces:** -1. Your setup code runs when your component is added to the page *(mounts)*. -2. After every re-render of your component where the dependencies have changed: - - First, your cleanup code runs with the old props and state. - - Then, your setup code runs with the new props and state. -3. Your cleanup code runs one final time after your component is removed from the page *(unmounts).* +1. Tu código de configuración se ejecuta cuando su componente se añade a la página *(montaje)*. +2. Después de cada re-renderización de tu componente donde las dependencias han cambiado: + - Primero, tu código de limpieza se ejecuta con las antiguas props y estados. + - Luego, tu código de configuración se ejecutará con las nuevas props y estados. +3. Tu código de limpieza se ejecutará una última vez después de que tu componente sea eliminado de la página *(desmontaje).* -**Let's illustrate this sequence for the example above.** +**Vamos a mostrar esta secuencia para el ejemplo anterior.** -When the `ChatRoom` component above gets added to the page, it will connect to the chat room with the initial `serverUrl` and `roomId`. If either `serverUrl` or `roomId` change as a result of a re-render (say, if the user picks a different chat room in a dropdown), your Effect will *disconnect from the previous room, and connect to the next one.* When the `ChatRoom` component is finally removed from the page, your Effect will disconnect one last time. +Cuando el componente `ChatRoom` se añade a la página, se conectará a la sala de conversación con `serverUrl` y `roomId`. Si cualquiera de los dos, `serverUrl` o `roomId` cambian como resultado de una re-renderización (digamos, si el usuario elige una sala de chat diferente en un desplegable), tu Efecto se *desconectará de la sala anterior, y se conectara a la siguiente.* Cuando el componente `ChatRoom` sea finalmente eliminado de la página, su efecto se desconectará por última vez. -**To [help you find bugs,](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) in development React runs setup and cleanup one extra time before the actual setup.** This is a stress-test that verifies your Effect's logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn't be able to distinguish between the setup being called once (as in production) and a *setup* → *cleanup* → *setup* sequence (as in development). [See common solutions.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +**Para [ayudarte a encontrar errores,](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) en el modo de desarrollo React ejecuta la configuración y la limpiezauna vez más antes de la configuración real.** Se trata de una prueba de estrés que verifica que la lógica de tu efecto se implementa correctamente. Si esto causa problemas visibles, tu función de limpieza está perdiendo algo de lógica. La función de limpieza debe detener o deshacer lo que la función de configuración estaba haciendo. La regla general es que el usuario no debería ser capaz de distinguir entre la configuración que se llama una vez (como en producción) y una secuencia de *configuración* → *limpieza* → *configuración* (como en desarrollo). [Échale un vistazo a las soluciones comunes.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) -**Try to [write every Effect as an independent process](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) and [only think about a single setup/cleanup cycle at a time.](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)** It shouldn't matter whether your component is mounting, updating, or unmounting. When your cleanup logic correctly "mirrors" the setup logic, your Effect will be resilient to running setup and cleanup as often as needed. +**Intenta [escribir cada efecto como un proceso independiente](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) y [sólo piensa en un único ciclo de montaje/limpieza a la vez.](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)** No debería importar si tu componente se está montando, actualizando o desmontando. Cuando tu lógica de limpieza es coherente con la lógica de configuración, tu Efecto será capaz de ejecutar la configuración y limpieza tantas veces como sea necesario. -An Effect lets you [keep your component synchronized](/learn/synchronizing-with-effects) with some external system (like a chat service). Here, *external system* means any piece of code that's not controlled by React, such as: +Un efecto te permite [mantener tu componente sincronizado](/learn/synchronizing-with-effects) con algún sistema externo (como un servicio de chat). Aquí, *sistema externo* significa cualquier pieza de código que no está controlado por React, como: -* A timer managed with [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) and [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval). -* An event subscription using [`window.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) and [`window.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener). -* A third-party animation library with an API like `animation.start()` and `animation.reset()`. +* Un temporizador gestionado con [`setInterval()`](https://developer.mozilla.org/es/docs/Web/API/setInterval) y [`clearInterval()`](https://developer.mozilla.org/es/docs/Web/API/clearInterval). +* Una suscripción de eventos usando [`window.addEventListener()`](https://developer.mozilla.org/es/docs/Web/API/EventTarget/addEventListener) y [`window.removeEventListener()`](https://developer.mozilla.org/es/docs/Web/API/EventTarget/removeEventListener). +* Una biblioteca de animación de terceros con una API como `animation.start()` y `animation.reset()`. -**If you're not connecting to any external system, [you probably don't need an Effect.](/learn/you-might-not-need-an-effect)** +**Si no estas conectado a ningún sistema externo, [probablemente no necesites un efecto.](/learn/you-might-not-need-an-effect)** - + -#### Connecting to a chat server {/*connecting-to-a-chat-server*/} +#### Conexión a un servidor de chat {/*connecting-to-a-chat-server*/} -In this example, the `ChatRoom` component uses an Effect to stay connected to an external system defined in `chat.js`. Press "Open chat" to make the `ChatRoom` component appear. This sandbox runs in development mode, so there is an extra connect-and-disconnect cycle, as [explained here.](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) Try changing the `roomId` and `serverUrl` using the dropdown and the input, and see how the Effect re-connects to the chat. Press "Close chat" to see the Effect disconnect one last time. +En este ejemplo, el componente `ChatRoom` utiliza un Efecto para permanecer conectado a un sistema externo definido en `chat.js`. Pulsa "Abrir chat" para que aparezca el componente `ChatRoom`. Este sandbox se ejecuta en modo de desarrollo, por lo que hay un ciclo extra de conexión y desconexión, como [se explica aquí.](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) Prueba a cambiar el `roomId` y `serverUrl` usando el desplegable y la entrada, y observa como el efecto se reconecta con el chat. Pulsa "Cerrar chat" para ver cómo el Efecto se desconectará por última vez. @@ -102,13 +156,13 @@ function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bienvenido al sitio {roomId}!

); } @@ -119,18 +173,18 @@ export default function App() { return ( <> {show &&
} {show && } @@ -141,13 +195,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando con el sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -162,9 +216,9 @@ button { margin-left: 10px; } -#### Listening to a global browser event {/*listening-to-a-global-browser-event*/} +#### Escuchar un evento global del navegador {/*listening-to-a-global-browser-event*/} -In this example, the external system is the browser DOM itself. Normally, you'd specify event listeners with JSX, but you can't listen to the global [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object this way. An Effect lets you connect to the `window` object and listen to its events. Listening to the `pointermove` event lets you track the cursor (or finger) position and update the red dot to move with it. +En este ejemplo, el sistema externo es el propio DOM del navegador. Normalmente, especificarías un event listener con JSX, pero no puedes escuchar al objeto [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) de esta manera. Un efecto te permite conectarte al objeto `window` y escuchar sus eventos. Escuchar el evento `pointermove` te permite seguir la posición del cursor (o del ratón) y actualizar el punto rojo para que se mueva con él. @@ -211,9 +265,9 @@ body { -#### Triggering an animation {/*triggering-an-animation*/} +#### Activación de una animación {/*triggering-an-animation*/} -In this example, the external system is the animation library in `animation.js`. It provides a JavaScript class called `FadeInAnimation` that takes a DOM node as an argument and exposes `start()` and `stop()` methods to control the animation. This component [uses a ref](/learn/manipulating-the-dom-with-refs) to access the underlying DOM node. The Effect reads the DOM node from the ref and automatically starts the animation for that node the component appears. +En este ejemplo, el sistema externo es la biblioteca de animación en `animation.js`. Proporciona una clase JavaScript llamada `FadeInAnimation` que toma un nodo del DOM como argumento y expone los métodos `start()` y `stop()` para controlar la animación. Este componente [utiliza una ref](/learn/manipulating-the-dom-with-refs) para acceder al nodo DOM subyacente. El Efecto lee el nodo de la ref y automáticamente inicia la animación para ese nodo que aparece en el componente. @@ -244,7 +298,7 @@ function Welcome() { backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)' }} > - Welcome + Bienvenido ); } @@ -254,7 +308,7 @@ export default function App() { return ( <>
{show && } @@ -271,11 +325,11 @@ export class FadeInAnimation { start(duration) { this.duration = duration; if (this.duration === 0) { - // Jump to end immediately + // Saltar al final inmediatamente this.onProgress(1); } else { this.onProgress(0); - // Start animating + // Comienza la animación this.startTime = performance.now(); this.frameId = requestAnimationFrame(() => this.onFrame()); } @@ -285,7 +339,7 @@ export class FadeInAnimation { const progress = Math.min(timePassed / this.duration, 1); this.onProgress(progress); if (progress < 1) { - // We still have more frames to paint + // Todavía tenemos que pintar más cuadros this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -310,9 +364,9 @@ html, body { min-height: 300px; } -#### Controlling a modal dialog {/*controlling-a-modal-dialog*/} +#### Controlar un cuadro de diálogo modal {/*controlling-a-modal-dialog*/} -In this example, the external system is the browser DOM. The `ModalDialog` component renders a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element. It uses an Effect to synchronize the `isOpen` prop to the [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) and [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close) method calls. +En este ejemplo, el sistema externo es el DOM del navegador. El componente `ModalDialog` renderiza un elemento [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog). Utiliza un efecto para sincronizar la prop `isOpen` con las llamadas a los métodos [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) y [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close). @@ -325,14 +379,14 @@ export default function App() { return ( <> - Hello there! + ¡Hola!
+ }}>Cerrar
); @@ -370,9 +424,9 @@ body { -#### Tracking element visibility {/*tracking-element-visibility*/} +#### Monitorear visibilidad de un elemento {/*tracking-element-visibility*/} -In this example, the external system is again the browser DOM. The `App` component displays a long list, then a `Box` component, and then another long list. Scroll the list down. Notice that when the `Box` component appears in the viewport, the background color changes to black. To implement this, the `Box` component uses an Effect to manage an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). This browser API notifies you when the DOM element is visible in the viewport. +En este Ejemplo, el sistema externo es de nuevo el DOM del navegador. El componente `App` muestra una lista larga, luego un componente `Box`, y luego otra lista larga. Desplaza la lista hacia abajo y observa que cuando el componente `Box` aparece en la pantalla, el color de fondo cambia a negro. Para implementar esto, el componente `Box` utiliza un efecto para gestionar un [`IntersectionObserver`](https://developer.mozilla.org/es/docs/Web/API/Intersection_Observer_API). Esta API del navegador te notifica cuando el elemento del DOM es visible en la pantalla. @@ -394,7 +448,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(
  • Item #{i} (keep scrolling)
  • ); + items.push(
  • Item #{i} (sigue desplazándote)
  • ); } return
      {items}
    } @@ -446,11 +500,11 @@ export default function Box() { --- -### Wrapping Effects in custom Hooks {/*wrapping-effects-in-custom-hooks*/} +### Envolver los efectos en Hooks personalizados {/*wrapping-effects-in-custom-hooks*/} -Effects are an ["escape hatch":](/learn/escape-hatches) you use them when you need to "step outside React" and when there is no better built-in solution for your use case. If you find yourself often needing to manually write Effects, it's usually a sign that you need to extract some [custom Hooks](/learn/reusing-logic-with-custom-hooks) for common behaviors that your components rely on. +Los efectos son una ["escotilla de escape":](/learn/escape-hatches) los usas cuando necesitas "salirte de React" y cuando no hay una mejor solución. Si te encuentras a menudo con la necesidad de escribir manualmente los efectos, suele ser una señal de que necesitas extraer algunos [Hooks personalizados](/learn/reusing-logic-with-custom-hooks) para los comportamientos comunes de los que dependen tus componentes. -For example, this `useChatRoom` custom Hook "hides" the logic of your Effect behind a more declarative API: +Por ejemplo, este Hook personalizado `useChatRoom` "esconde" la lógica de su efecto detrás de una API más declarativa: ```js {1,11} function useChatRoom({ serverUrl, roomId }) { @@ -466,7 +520,7 @@ function useChatRoom({ serverUrl, roomId }) { } ``` -Then you can use it from any component like this: +Entonces puedes usarlo desde cualquier componente como este: ```js {4-7} function ChatRoom({ roomId }) { @@ -479,15 +533,15 @@ function ChatRoom({ roomId }) { // ... ```` -There are also many excellent custom Hooks for every purpose available in the React ecosystem. +También hay muchos excelentes Hooks personalizados para cada propósito disponibles en el ecosistema de React. -[Learn more about wrapping Effects in custom Hooks.](/learn/reusing-logic-with-custom-hooks) +[Más información sobre cómo envolver los efectos en Hooks personalizados.](/learn/reusing-logic-with-custom-hooks) - + -#### Custom `useChatRoom` Hook {/*custom-usechatroom-hook*/} +#### Hook personalizado `useChatRoom` {/*custom-usechatroom-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Este ejemplo es idéntico a uno de los [ejemplos anteriores,](#examples-connecting) pero la lógica se extrae a un Hook personalizado. @@ -506,13 +560,13 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenido al sitio {roomId}!

    ); } @@ -523,18 +577,18 @@ export default function App() { return ( <> {show &&
    } {show && } @@ -560,13 +614,13 @@ export function useChatRoom({ serverUrl, roomId }) { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando con el sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado del sitio"' + roomId + '" en ' + serverUrl); } }; } @@ -581,9 +635,9 @@ button { margin-left: 10px; } -#### Custom `useWindowListener` Hook {/*custom-usewindowlistener-hook*/} +#### Hook personalizado `useWindowListener` {/*custom-usewindowlistener-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Este ejemplo es idéntico a uno de los [ejemplos anteriores,](#examples-connecting) pero la lógica se extrae a un hook personalizado. @@ -638,9 +692,9 @@ body { -#### Custom `useIntersectionObserver` Hook {/*custom-useintersectionobserver-hook*/} +#### Hook personalizado `useIntersectionObserver` {/*custom-useintersectionobserver-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is partially extracted to a custom Hook. +Este ejemplo es idéntico a uno de los [ejemplos anteriores,](#examples-connecting) pero la lógica está parcialmente extraída a un Hook personalizado. @@ -662,7 +716,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(
  • Item #{i} (keep scrolling)
  • ); + items.push(
  • Item #{i} (continúa bajando)
  • ); } return
      {items}
    } @@ -730,11 +784,11 @@ export function useIntersectionObserver(ref) { --- -### Controlling a non-React widget {/*controlling-a-non-react-widget*/} +### Controlar un widget que no sea de React {/*controlling-a-non-react-widget*/} -Sometimes, you want to keep an external system synchronized to some prop or state of your component. +A veces, quieres mantener un sistema externo sincronizado con alguna prop o estado de tu componente. -For example, if you have a third-party map widget or a video player component written without React, you can use an Effect to call methods on it that make its state match the current state of your React component. This Effect creates an instance of a `MapWidget` class defined in `map-widget.js`. When you change the `zoomLevel` prop of the `Map` component, the Effect calls the `setZoom()` on the class instance to keep it synchronized: +Por ejemplo, si tienes un widget de mapa de terceros o un componente reproductor de vídeo escrito sin React, puedes usar un Effect para llamar a los métodos en él que hagan que su estado coincida con el estado actual de tu componente de React. Este efecto crea una instancia de la clase `MapWidget` definida en `map-widget.js`. Cuando cambias la propiedad `zoomLevel` del componente `Map`, el efecto llama a `setZoom()` en la instancia de la clase para mantenerla sincronizada: @@ -764,7 +818,7 @@ export default function App() { const [zoomLevel, setZoomLevel] = useState(0); return ( <> - Zoom level: {zoomLevel}x + Nivel de Zoom: {zoomLevel}x
    @@ -834,15 +888,15 @@ button { margin: 5px; }
    -In this example, a cleanup function is not needed because the `MapWidget` class manages only the DOM node that was passed to it. After the `Map` React component is removed from the tree, both the DOM node and the `MapWidget` class instance will be automatically garbage-collected by the browser JavaScript engine. +En este ejemplo, no se necesita una función de limpieza porque la clase `MapWidget` solo gestiona el nodo DOM que se le pasó. Después de que el componente de React `Map` se elimine del árbol, tanto el nodo del DOM como la instancia de la clase `MapWidget` serán eliminados automáticamente por el _garbage-collector_ del motor de JavaScript de tu navegador. --- -### Fetching data with Effects {/*fetching-data-with-effects*/} +### Obtención de datos con Efectos {/*fetching-data-with-effects*/} -You can use an Effect to fetch data for your component. Note that [if you use a framework,](/learn/start-a-new-react-project#building-with-a-full-featured-framework) using your framework's data fetching mechanism will be a lot more efficient than writing Effects manually. +Puedes utilizar un efecto para obtener datos para tu componente. Ten en cuenta que [si utilizas un framework,](/learn/start-a-new-react-project#building-with-a-full-featured-framework) usar el mecanismo de datos de tu framework será mucho más eficiente que escribir los efectos manualmente. -If you want to fetch data from an Effect manually, your code might look like this: +Si quieres obtener datos con un Efecto manualmente, tu código podría verse así: ```js import { useState, useEffect } from 'react'; @@ -868,7 +922,7 @@ export default function Page() { // ... ``` -Note the `ignore` variable which is initialized to `false`, and is set to `true` during cleanup. This ensures [your code doesn't suffer from "race conditions":](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) network responses may arrive in a different order than you sent them. +Observa que la variable `ignore` se inicializa con `false`, y se reasigna a `true` durante la limpieza. Esto asegura que [tu código no sufra de "condiciones de carrera":](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) las respuestas de la red pueden llegar en un orden diferente al que las enviaste. @@ -902,7 +956,7 @@ export default function Page() {
    -

    {bio ?? 'Loading...'}

    +

    {bio ?? 'Cargando...'}

    ); } @@ -913,7 +967,7 @@ export async function fetchBio(person) { const delay = person === 'Bob' ? 2000 : 200; return new Promise(resolve => { setTimeout(() => { - resolve('This is ' + person + '’s bio.'); + resolve('Esta es la biografía de ' + person ); }, delay); }) } @@ -921,7 +975,7 @@ export async function fetchBio(person) {
    -You can also rewrite using the [`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) syntax, but you still need to provide a cleanup function: +También puedes reescribirlo usando la sintaxis [`async` / `await`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/async_function), pero igualmente necesitarás proporcionar una función de limpieza: @@ -958,7 +1012,7 @@ export default function Page() {
    -

    {bio ?? 'Loading...'}

    +

    {bio ?? 'Cargando...'}

    ); } @@ -969,7 +1023,7 @@ export async function fetchBio(person) { const delay = person === 'Bob' ? 2000 : 200; return new Promise(resolve => { setTimeout(() => { - resolve('This is ' + person + '’s bio.'); + resolve('Esta es la biografía de ' + person ); }, delay); }) } @@ -977,124 +1031,126 @@ export async function fetchBio(person) {
    -Writing data fetching directly in Effects gets repetitive and makes it difficult to add optimizations like caching and server rendering later. [It's easier to use a custom Hook--either your own or maintained by the community.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) +Escribir la obtención de datos directamente en los Efectos se vuelve repetitivo y dificulta la adición de optimizadores como el almacenamiento en caché y el renderizado de lado del servidor más adelante. [Es más fácil utilizar un Hook personalizado, ya sea propio o hecho por la comunidad.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) + + - +#### ¿Cuáles son las mejores alternativas a la obtención de datos con Efectos? {/*what-are-good-alternatives-to-data-fetching-in-effects*/} -Writing `fetch` calls inside Effects is a [popular way to fetch data](https://www.robinwieruch.de/react-hooks-fetch-data/), especially in fully client-side apps. This is, however, a very manual approach and it has significant downsides: +Escribir llamadas `fetch` dentro de Efectos es una forma [popular de obtener datos](https://www.robinwieruch.de/react-hooks-fetch-data/), especialmente en aplicaciones totalmente del lado del cliente. Sin embargo, este es un enfoque muy manual y tiene importantes desventajas: -- **Effects don't run on the server.** This means that the initial server-rendered HTML will only include a loading state with no data. The client computer will have to download all JavaScript and render your app only to discover that now it needs to load the data. This is not very efficient. -- **Fetching directly in Effects makes it easy to create "network waterfalls".** You render the parent component, it fetches some data, renders the child components, and then they start fetching their data. If the network is not very fast, this is significantly slower than fetching all data in parallel. -- **Fetching directly in Effects usually means you don't preload or cache data.** For example, if the component unmounts and then mounts again, it would have to fetch the data again. -- **It's not very ergonomic.** There's quite a bit of boilerplate code involved when writing `fetch` calls in a way that doesn't suffer from bugs like [race conditions.](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) +- **Los efectos no se ejecutan en el servidor.** Esto significa que el HTML renderizado inicialmente en el servidor sólo incluirá un estado de carga sin datos. El ordenador del cliente tendrá que descargar todo el JavaScript y renderizar tu aplicación sólo para descubrir que ahora necesita cargar los datos. Esto no es muy eficiente. +- **La obtención de datos directamente en Efectos facilita la creación de "cascadas de red" (_network waterfalls_).** Se renderiza el componente padre, se obtienen algunos datos, luego se renderizan los componentes hijos, y luego ellos hacen lo mismo. Si la red no es muy rápida, este proceso secuencial es significativamente más lento que obtener todos los datos en paralelo de una sola vez. +- **La obtención de datos directamente en Efectos suele significar que no se pre-cargan ni se almacenan en caché los datos.** Por ejemplo, si el componente se desmonta y se vuelve a montar, tendría que obtener los datos de nuevo. +- **No es muy ergonómico.** Hay bastante código _boilerplate_ al hacer llamadas `fetch` de tal manera que no sufra de errores como las [condiciones de carrera.](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) -This list of downsides is not specific to React. It applies to fetching data on mount with any library. Like with routing, data fetching is not trivial to do well, so we recommend the following approaches: +Esta lista de inconvenientes no es específica de React. Se aplica a la obtención de datos en el montaje con cualquier biblioteca. Al igual que con el enrutamiento, la obtención de datos no es trivial para hacerlo bien, por lo que recomendamos los siguientes enfoques: -- **If you use a [framework](/learn/start-a-new-react-project#building-with-a-full-featured-framework), use its built-in data fetching mechanism.** Modern React frameworks have integrated data fetching mechanisms that are efficient and don't suffer from the above pitfalls. -- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://react-query.tanstack.com/), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood but also add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes). +- **Si usas un [framework](/learn/start-a-new-react-project#building-with-a-full-featured-framework), utiliza su mecanismo de obtención de datos integrado.** Los frameworks modernos de React han integrado mecanismos de obtención de datos que son eficientes y no sufren los inconvenientes anteriores. +- **De lo contrario, considera la posibilidad de utilizar o construir una caché del lado del cliente.** Las soluciones populares de código abierto incluyen [React Query](https://react-query.tanstack.com/), [useSWR](https://swr.vercel.app/), y [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) También puedes crear tu propia solución, en cuyo caso se usarían Efectos por debajo, pero también se añadiría lógica para deduplicar las peticiones, almacenar en caché las respuestas y evitar las cascadas de red (pre-cargando los datos o elevando los requisitos de datos a las rutas). -You can continue fetching data directly in Effects if neither of these approaches suit you. +Puedes seguir obteniendo datos directamente en Efectos si ninguno de estos enfoques te conviene. --- -### Specifying reactive dependencies {/*specifying-reactive-dependencies*/} +### Especificación de dependencias reactivas {/*specifying-reactive-dependencies*/} -**Notice that you can't "choose" the dependencies of your Effect.** Every reactive value used by your Effect's code must be declared as a dependency. Your Effect's dependency list is determined by the surrounding code: +**Observa que no puedes "elegir" las dependencias de tu Efecto.** Cada valor reactivo utilizado por el código de tu efecto debe ser declarado como una dependencia. La lista de dependencias de tu efecto está determinada por el código que lo rodea: ```js [[2, 1, "roomId"], [2, 2, "serverUrl"], [2, 5, "serverUrl"], [2, 5, "roomId"], [2, 8, "serverUrl"], [2, 8, "roomId"]] -function ChatRoom({ roomId }) { // This is a reactive value - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too +function ChatRoom({ roomId }) { // Este es un valor reactivo + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // Este es también un valor reactivo useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values + const connection = createConnection(serverUrl, roomId); // Este efecto lee estos valores reactivos connection.connect(); return () => connection.disconnect(); - }, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect + }, [serverUrl, roomId]); // ✅ Así que debes especificarlos como dependencias de tu Efecto // ... } ``` -If either `serverUrl` or `roomId` change, your Effect will reconnect to the chat using the new values. +Si el `serverUrl` o el `roomId` cambian, tu efecto se reconectará al chat usando los nuevos valores. -**[Reactive values](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) include props and all variables and functions declared directly inside of your component.** Since `roomId` and `serverUrl` are reactive values, you can't remove them from the dependency list. If you try to omit them and [your linter is correctly configured for React,](/learn/editor-setup#linting) the linter will flag this as a mistake that you need to fix: +**Los [valores reactivos](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) incluyen props y todas las variables y funciones declaradas directamente dentro de su componente.** Como `roomId` y `serverUrl` son valores reactivos, no puedes eliminarlos de la lista de dependencias. Si intentas omitirlos y [tu linter está correctamente configurado para React,](/learn/editor-setup#linting) el linter lo marcará como un error que debes corregir: ```js {8} -const serverUrl = 'https://localhost:1234'; - function ChatRoom({ roomId }) { + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); + useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // 🔴 React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl' + }, []); // 🔴 React Hook useEffect tiene dependencias faltantes: 'roomId' y 'serverUrl' // ... } ``` -**To remove a dependency, you need to ["prove" to the linter that it *doesn't need* to be a dependency.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)** For example, you can move `serverUrl` out of your component to prove that it's not reactive and won't change on re-renders: +**Para eliminar una dependencia, tienes que ["demostrar" al linter que *no necesita* ser una dependencia.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)** Por ejemplo, puedes mover `serverUrl` fuera de tu componente para demostrar que no es reactivo y que no cambiará en las re-renderizaciones: ```js {1,8} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Ya no es un valor reactivo function ChatRoom({ roomId }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas las dependencias declaradas // ... } ``` -Now that `serverUrl` is not a reactive value (and can't change on a re-render), it doesn't need to be a dependency. **If your Effect's code doesn't use any reactive values, its dependency list should be empty (`[]`):** +Ahora que `serverUrl` no es un valor reactivo (y no puede cambiar en una renderización), no necesita ser una dependencia. **Si el código de tu efecto no utiliza ningún valor reactivo, su lista de dependencias debería estar vacía (`[]`):** ```js {1,2,9} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore -const roomId = 'music'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Ya no es un valor reactivo +const roomId = 'music'; // Ya no es un valor reactivo function ChatRoom() { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Todas las dependencias declaradas // ... } ``` -[An Effect with empty dependencies](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) doesn't re-run when any of your component's props or state change. +[Un efecto con dependencias](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) vacías no se vuelve a ejecutar cuando cambian las props o el estado del componente. -If you have an existing codebase, you might have some Effects that suppress the linter like this: +Si tienes una base de código existente, puede que tengas algunos efectos que supriman el linter de esta manera: ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 Evite suprimir el linter de esta manera: // eslint-ignore-next-line react-hooks/exhaustive-dependencies }, []); ``` -**When dependencies don't match the code, there is a high risk of introducing bugs.** By suppressing the linter, you "lie" to React about the values your Effect depends on. [Instead, prove they're unnecessary.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies) +**Cuando las dependencias no coinciden con el código, existe un alto riesgo de introducir errores.** Al suprimir el linter, le "mientes" a React sobre los valores de los que depende tu efecto. [En su lugar, demuestra que son innecesarios.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies) - + -#### Passing a dependency array {/*passing-a-dependency-array*/} +#### Pasar un _array_ de dependencias {/*passing-a-dependency-array*/} -If you specify the dependencies, your Effect runs **after the initial render _and_ after re-renders with changed dependencies.** +Si especificas las dependencias, su Efecto se ejecuta **después de la renderización inicial _y_ después de las re-renderizaciones con las dependencias cambiadas.** ```js {3} useEffect(() => { // ... -}, [a, b]); // Runs again if a or b are different +}, [a, b]); // Se ejecuta de nuevo si a o b son diferentes ``` -In the below example, `serverUrl` and `roomId` are [reactive values,](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) so they both must be specified as dependencies. As a result, selecting a different room in the dropdown or editing the server URL input causes the chat to re-connect. However, since `message` isn't used in the Effect (and so it isn't a dependency), editing the message doesn't re-connect to the chat. +En el siguiente ejemplo, `serverUrl` y `roomId` son [valores reactivos,](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) por lo que ambos deben ser especificados como dependencias. Como resultado, la selección de un sitio diferente en el menú desplegable o la edición de la entrada de la URL del servidor hace que el chat se vuelva a conectar. Sin embargo, dado que `message` no se utiliza en el efecto (y por tanto no es una dependencia), la edición del mensaje no reconecta el chat. @@ -1117,15 +1173,15 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    ¡Bienvenido al sitio {roomId}!

    @@ -1138,17 +1194,17 @@ export default function App() { return ( <> {show &&
    } @@ -1160,13 +1216,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conexión a el sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -1181,20 +1237,20 @@ button { margin-left: 5px; } -#### Passing an empty dependency array {/*passing-an-empty-dependency-array*/} +#### Pasar un _array_ de dependencias vacío {/*passing-an-empty-dependency-array*/} -If your Effect truly doesn't use any reactive values, it will only run **after the initial render.** +Si tu efecto realmente no utiliza ningún valor reactivo, sólo se ejecutará **después del renderizado inicial.** ```js {3} useEffect(() => { // ... -}, []); // Does not run again (except once in development) +}, []); // No se vuelve a ejecutar (excepto una vez más en el modo de desarrollo) ``` -**Even with empty dependencies, setup and cleanup will [run one extra time in development](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) to help you find bugs.** +**Incluso con dependencias vacías, la configuración y la limpieza [se ejecutarán una vez más en desarrollo](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) para ayudarte a encontrar errores.** -In this example, both `serverUrl` and `roomId` are hardcoded. Since they're declared outside the component, they are not reactive values, and so they aren't dependencies. The dependency list is empty, so the Effect doesn't re-run on re-renders. +En este ejemplo, tanto `serverUrl` como `roomId` están "_hard-codeados_". Como están declarados fuera del componente, no son valores reactivos y, por lo tanto, no son dependencias. La lista de dependencias está vacía, por lo que el Efecto no se vuelve a ejecutar en las re-renderizaciones. @@ -1216,9 +1272,9 @@ function ChatRoom() { return ( <> -

    Welcome to the {roomId} room!

    +

    ¡Bienvenido al sitio {roomId}!

    @@ -1230,7 +1286,7 @@ export default function App() { return ( <> {show &&
    } {show && } @@ -1241,13 +1297,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando a el sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -1258,17 +1314,17 @@ export function createConnection(serverUrl, roomId) { -#### Passing no dependency array at all {/*passing-no-dependency-array-at-all*/} +#### No pasar ningún _array_ de dependencias {/*passing-no-dependency-array-at-all*/} -If you pass no dependency array at all, your Effect runs **after every single render (and re-render)** of your component. +Si no pasas ninguna matriz de dependencia, tu Efecto se ejecuta **después de cada renderización (y re-renderización)** de tu componente. ```js {3} useEffect(() => { // ... -}); // Always runs again +}); // Siempre se vuelve a ejecutar ``` -In this example, the Effect re-runs when you change `serverUrl` and `roomId`, which is sensible. However, it *also* re-runs when you change the `message`, which is probably undesirable. This is why usually you'll specify the dependency array. +En este ejemplo, el Efecto se vuelve a ejecutar cuando se cambia `serverUrl` y `roomId`, lo cual es razonable. Sin embargo, también se vuelve a ejecutar cuando cambias el `message`, lo que probablemente no es deseable. Por eso, normalmente se especifica el _array_ de dependencias. @@ -1286,20 +1342,20 @@ function ChatRoom({ roomId }) { return () => { connection.disconnect(); }; - }); // No dependency array at all + }); // No hay ningún array de dependecias return ( <> -

    Welcome to the {roomId} room!

    +

    Bienvenido al sitio {roomId}!

    @@ -1318,11 +1374,11 @@ export default function App() { onChange={e => setRoomId(e.target.value)} > - - + + {show &&
    } @@ -1334,13 +1390,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando al sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectando del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -1359,9 +1415,9 @@ button { margin-left: 5px; } --- -### Updating state based on previous state from an Effect {/*updating-state-based-on-previous-state-from-an-effect*/} +### Actualización del estado basado en el estado anterior de un efecto {/*updating-state-based-on-previous-state-from-an-effect*/} -When you want to update state based on previous state from an Effect, you might run into a problem: +Cuando quieras actualizar el estado basándote en el estado anterior de un Efecto, puedes encontrarte con un problema: ```js {6,9} function Counter() { @@ -1369,17 +1425,17 @@ function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(count + 1); // You want to increment the counter every second... + setCount(count + 1); // Quieres incrementar el contador cada segundo... }, 1000) return () => clearInterval(intervalId); - }, [count]); // 🚩 ... but specifying `count` as a dependency always resets the interval. + }, [count]); // 🚩 ... pero al especificar `count` como dependencia siempre se restablece el intervalo. // ... } ``` -Since `count` is a reactive value, it must be specified in the list of dependencies. However, that causes the Effect to cleanup and setup again every time the `count` changes. This is not ideal. +Como `count` es un valor reactivo, debe ser especificado en la lista de dependencias. Sin embargo, eso hace que el Efecto se limpie y se configure de nuevo cada vez que `count` cambia. Esto resulta inadecuado. -To fix this, [pass the `c => c + 1` state updater](/apis/react/useState#updating-state-based-on-the-previous-state) to `setCount`: +Para solucionar esto, [pásale el actualizador de estado `c => c + 1`](/reference/react/useState#updating-state-based-on-the-previous-state) a `setCount`: @@ -1391,10 +1447,10 @@ export default function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(c => c + 1); // ✅ Pass a state updater + setCount(c => c + 1); // ✅ Pasar un actualizador de estado }, 1000); return () => clearInterval(intervalId); - }, []); // ✅ Now count is not a dependency + }, []); // ✅ Ahora count no es una dependencia return

    {count}

    ; } @@ -1414,14 +1470,14 @@ body {
    -Now that you're passing `c => c + 1` instead of `count + 1`, [your Effect no longer needs to depend on `count`.](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state) As a result of this fix, it won't need to cleanup and setup the interval again every time the `count` changes. +Ahora que pasas `c => c + 1` en lugar de `count + 1`, [tu Efecto ya no necesita depender de un `count`.](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state) Como resultado de esta corrección, no tendrá que limpiar y configurar el intervalo de nuevo cada vez que el recuento cambia. --- -### Removing unnecessary object dependencies {/*removing-unnecessary-object-dependencies*/} +### Eliminación de dependencias de objetos innecesarios {/*removing-unnecessary-object-dependencies*/} -If your Effect depends on an object or a function created during rendering, it might run more often than needed. For example, this Effect re-connects after every render because the `options` object is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) +Si tu Efecto depende de un objeto o de una función creada durante el renderizado, puede que se ejecute con más frecuencia de la necesaria. Por ejemplo, este Efecto se reconecta después de cada renderización porque el objeto `options` es [diferente para cada renderización:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) ```js {6-9,12,15} const serverUrl = 'https://localhost:1234'; @@ -1429,20 +1485,20 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - const options = { // 🚩 This object is created from scratch on every re-render + const options = { // 🚩 Este objeto se crea desde cero en cada re-renderización serverUrl: serverUrl, roomId: roomId }; useEffect(() => { - const connection = createConnection(options); // It's used inside the Effect + const connection = createConnection(options); // Se usa dentro del Efecto connection.connect(); return () => connection.disconnect(); - }, [options]); // 🚩 As a result, these dependencies are always different on a re-render + }, [options]); // 🚩 Como resultado, estas dependencias son siempre diferentes en una renderización // ... ``` -Avoid using an object created during rendering as a dependency. Instead, create the object inside the Effect: +Evita utilizar como dependencia un objeto creado durante la renderización. En su lugar, crea el objeto dentro del Efecto: @@ -1467,7 +1523,7 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    ¡Bienvenido al sitio {roomId}!

    setMessage(e.target.value)} /> ); @@ -1484,8 +1540,8 @@ export default function App() { onChange={e => setRoomId(e.target.value)} > - - + +
    @@ -1497,13 +1553,13 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando con el sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -1516,21 +1572,21 @@ button { margin-left: 10px; }
    -Now that you create the `options` object inside the Effect, the Effect itself only depends on the `roomId` string. +Ahora que creas el objeto `options` dentro del Efecto, el propio Efecto sólo depende del string `roomId`. -With this fix, typing into the input doesn't reconnect the chat. Unlike an object which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Con esta solución, escribir en la entrada no reconecta el chat. A diferencia de un objeto que se vuelve a crear, una _string_ como `roomId` no cambia a menos que la establezcas con otro valor. [Más información sobre la eliminación de dependencias.](/learn/removing-effect-dependencies) --- -### Removing unnecessary function dependencies {/*removing-unnecessary-function-dependencies*/} +### Eliminación de dependencias de funciones innecesarias {/*removing-unnecessary-function-dependencies*/} -If your Effect depends on an object or a function created during rendering, it might run more often than needed. For example, this Effect re-connects after every render because the `options` object is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) +Si tu Efecto depende de un objeto o de una función creada durante el renderizado, puede que se ejecute con más frecuencia de la necesaria. Por ejemplo, este Efecto se reconecta después de cada renderización porque la función `createOptions` es [diferente para cada renderización:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) ```js {4-9,12,16} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - function createOptions() { // 🚩 This function is created from scratch on every re-render + function createOptions() { // 🚩 Esta función se crea desde cero en cada renderización return { serverUrl: serverUrl, roomId: roomId @@ -1538,17 +1594,17 @@ function ChatRoom({ roomId }) { } useEffect(() => { - const options = createOptions(); // It's used inside the Effect + const options = createOptions(); // Se usa dentro del Efecto const connection = createConnection(); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // 🚩 As a result, these dependencies are always different on a re-render + }, [createOptions]); // 🚩 Como resultado, estas dependencias son siempre diferentes en una renderización // ... ``` -By itself, creating a function from scratch on every re-render is not a problem. You don't need to optimize that. However, if you use it as a dependency of your Effect, it will cause your Effect to re-run after every re-render. +Por sí mismo, crear una función desde cero en cada renderización no es un problema. No es necesario optimizar eso. Sin embargo, si lo usas como una dependencia de tu Efecto, hará que tu Efecto se vuelva a ejecutar después de cada re-renderización. -Avoid using a function created during rendering as a dependency. Instead, declare it inside the Effect: +Evita utilizar como dependencia una función creada durante el renderizado. En su lugar, declárala dentro del Efecto: @@ -1577,7 +1633,7 @@ function ChatRoom({ roomId }) { return ( <> -

    Welcome to the {roomId} room!

    +

    ¡Bienvenido al sitio {roomId}!

    setMessage(e.target.value)} /> ); @@ -1588,14 +1644,14 @@ export default function App() { return ( <>
    @@ -1607,13 +1663,13 @@ export default function App() { ```js chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Una implementación real se conectaría al servidor return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando al sitio "' + roomId + '" en ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectando del sitio "' + roomId + '" en ' + serverUrl); } }; } @@ -1626,58 +1682,58 @@ button { margin-left: 10px; }
    -Now that you define the `createOptions` function inside the Effect, the Effect itself only depends on the `roomId` string. With this fix, typing into the input doesn't reconnect the chat. Unlike a function which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Ahora que se define la función `createOptions` function inside the Effect, dentro del Efecto, el Efecto mismo sólo depende del string `roomId`. Con esta solución, escribir en la entrada no reconecta el chat. A diferencia de una función que se vuelve a crear, una _string_ como `roomId` no cambia a menos que la establezcas con otro valor. [Lee más sobre la eliminación de dependencias.](/learn/removing-effect-dependencies) --- -### Reading the latest props and state from an Effect {/*reading-the-latest-props-and-state-from-an-effect*/} +### Lectura de las últimas props y el estado desde un Efecto {/*reading-the-latest-props-and-state-from-an-effect*/} -This section describes an **experimental API that has not yet been added to React,** so you can't use it yet. +Esta sección describe una **API experimental que aún no se ha añadido a React,** por lo que todavía no se puede utilizar. -By default, when you read a reactive value from an Effect, you have to add it as a dependency. This ensures that your Effect "reacts" to every change of that value. For most dependencies, that's the behavior you want. +Por defecto, cuando lees un valor reactivo de un Efecto, tienes que añadirlo como una dependencia. Esto asegura que tu Efecto "reacciona" a cada cambio de ese valor. Para la mayoría de las dependencias, ese es el comportamiento que quieres. -**However, sometimes you'll want to read the *latest* props and state from an Effect without "reacting" to them.** For example, imagine you want to log the number of the items in the shopping cart for every page visit: +**Sin embargo, a veces querrá leer las *últimas* props y estados de un efecto sin "reaccionar" a ellos.** Por ejemplo, imagina que quieres registrar el número de artículos del carrito de compras en cada visita a la página: ```js {3} function Page({ url, shoppingCart }) { useEffect(() => { logVisit(url, shoppingCart.length); - }, [url, shoppingCart]); // ✅ All dependencies declared + }, [url, shoppingCart]); // ✅ Todas las dependencias declaradas // ... } ``` -**What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. To do this, [declare an *Event function*](/learn/separating-events-from-effects#declaring-an-event-function) with the [`useEvent`](/apis/react/useEvent) Hook, and move the code that reads `shoppingCart` inside of it: +**¿Qué pasa si quieres registrar una nueva visita a la página después de cada cambio de `url`, pero *no* si sólo cambia el `shoppingCart`?** No puedes excluir `shoppingCart` de las dependencias sin romper las [reglas de reactividad.](#specifying-reactive-dependencies) Sin embargo, puedes expresar que *no quieres* que una parte de código "reaccione" a los cambios aunque sea llamado desde dentro de un Efecto. Para hacer esto, [declara un *Efecto de evento*](/learn/separating-events-from-effects#declaring-an-effect-event) con el Hook [`useEffectEvent`](/reference/react/useEffectEvent), y mueve el código que lea al `shoppingCart` dentro de tal Hook: ```js {2-4,7,8} function Page({ url, shoppingCart }) { - const onVisit = useEvent(visitedUrl => { + const onVisit = useEffectEvent(visitedUrl => { logVisit(visitedUrl, shoppingCart.length) }); useEffect(() => { onVisit(url); - }, [url]); // ✅ All dependencies declared + }, [url]); // ✅ Todas las dependencias declaradas // ... } ``` -**Event functions are not reactive and don't need to be specified as dependencies of your Effect.** This is what lets you put non-reactive code (where you can read the latest value of some props and state) inside of them. For example, by reading `shoppingCart` inside of `onVisit`, you ensure that `shoppingCart` won't re-run your Effect. +**Los Eventos de efecto no son reactivos y nunca serán especificados como dependencias de tu Efecto.** Esto es lo que te permite poner código no reactivo (donde puedes leer el último valor de algunas props y estados) dentro de ellos. Por ejemplo, al leer `shoppingCart` dentro de `onVisit`, te aseguras de que `shoppingCart` no vuelva a ejecutar tu efecto. En el futuro, el linter tendrá soporte para useEffectEvent y comprobará que se omiten los Eventos de efectos de las dependencias. -[Read more about how Event functions let you separate reactive and non-reactive code.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-event-functions) +[Lee más sobre cómo los eventos de efecto te permiten separar el código reactivo del no reactivo.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) --- -### Displaying different content on the server and the client {/*displaying-different-content-on-the-server-and-the-client*/} +### Mostrar contenidos diferentes en el servidor y en el cliente {/*displaying-different-content-on-the-server-and-the-client*/} -If your app uses server rendering (either [directly](/apis/react-dom/server) or via a [framework](/learn/start-a-new-react-project#building-with-a-full-featured-framework)), your component will render in two different environments. On the server, it will render to produce the initial HTML. On the client, React will run the rendering code again so that it can attach your event handlers to that HTML. This is why, for [hydration](/apis/react-dom/client/hydrateRoot#hydrating-server-rendered-html) to work, your initial render output must be identical on the client and the server. +Si tu aplicación utiliza renderizado de lado del servidor (ya sea [directamente](/reference/react-dom/server) o a través de un [framework](/learn/start-a-new-react-project#building-with-a-full-featured-framework)), tu componente se renderizará en dos entornos diferentes. En el servidor, se renderizará para producir el HTML inicial. En el cliente, React ejecutará de nuevo el código de renderizado para poder adjuntar tus manejadores de eventos a ese HTML. Por eso, para que la [hidratación](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) funcione, tu salida inicial de renderizado debe ser idéntica en el cliente y en el servidor. -In rare cases, you might need to display different content on the client. For example, if your app reads some data from [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), it can't possibly do that on the server. Here is how you would typically implement this: +En raras ocasiones, es posible que necesites mostrar un contenido diferente en el cliente. Por ejemplo, si su aplicación lee algunos datos del [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), no puede hacerlo en el servidor. Así es como típicamente se implementaría esto: ```js function MyComponent() { @@ -1688,96 +1744,44 @@ function MyComponent() { }, []); if (didMount) { - // ... return client-only JSX ... + // ... devolver JSX sólo para clientes ... } else { - // ... return initial JSX ... + // ... devolver el JSX inicial ... } } ``` -While the app is loading, the user will see the initial render output. Then, when it's loaded and hydrated, your Effect will run and set `didMount` to `true`, triggering a re-render. This will switch to the client-only render output. Note that Effects don't run on the server, so this is why `didMount` was `false` during the initial server render. - -Use this pattern sparingly. Keep in mind that users with a slow connection will see the initial content for quite a bit of time--potentially, many seconds--so you don't want to make jarring changes to your component's appearance. In many cases, you can avoid the need for this by conditionally showing different things with CSS. - ---- - -## Reference {/*reference*/} - -### `useEffect(setup, dependencies?)` {/*useeffect*/} - -Call `useEffect` at the top level of your component to declare an Effect: - -```js -import { useEffect } from 'react'; -import { createConnection } from './chat.js'; - -function ChatRoom({ roomId }) { - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - - useEffect(() => { - const connection = createConnection(serverUrl, roomId); - connection.connect(); - return () => { - connection.disconnect(); - }; - }, [serverUrl, roomId]); - // ... -} -``` - -[See more examples above.](#examples-connecting) - -#### Parameters {/*parameters*/} - -* `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. When your component is first added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function one last time. - -* **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison algorithm. If you don't specify the dependencies at all, your Effect will re-run after every re-render of the component. [See the difference between passing an array of dependencies, an empty array, and no dependencies at all.](#examples-dependencies) - -#### Returns {/*returns*/} - -`useEffect` returns `undefined`. - -#### Caveats {/*caveats*/} - -* `useEffect` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. - -* If you're **not trying to synchronize with some external system,** [you probably don't need an Effect.](/learn/you-might-not-need-an-effect) - -* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, [you need to implement the cleanup function.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) - -* If some of your dependencies are objects or functions defined inside the component, there is a risk that they will **cause the Effect to re-run more often than needed.** To fix this, remove unnecessary [object](#removing-unnecessary-object-dependencies) and [function](#removing-unnecessary-function-dependencies) dependencies. You can also [extract state updates](#updating-state-based-on-previous-state-from-an-effect) and [non-reactive logic](#reading-the-latest-props-and-state-from-an-effect) outside of your Effect. - -* If your Effect wasn't caused by an interaction (like a click), React will let the browser **paint the updated screen first before running your Effect.** If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), you need to replace `useEffect` with [`useLayoutEffect`.](/apis/react/useLayoutEffect) +Mientras se carga la aplicación, el usuario verá la salida de renderización inicial. Luego, cuando esté cargada e hidratada, tu efecto se ejecutará y establecerá `didMount` a `true`, disparando una re-renderización. Esto cambiará a la salida de renderización sólo para el cliente. Ten en cuenta que los Efectos no se ejecutan en el servidor, por eso `didMount` era `false` durante el renderizado inicial del servidor. -* Effects **only run on the client.** They don't run during server rendering. +Utiliza este patrón con moderación. Ten en cuenta que los usuarios con una conexión lenta verán el contenido inicial durante bastante tiempo -potencialmente, muchos segundos- por lo que no querrás hacer cambios bruscos en la apariencia de tu componente. En muchos casos, puedes evitar la necesidad de esto mostrando condicionalmente diferentes cosas con CSS. --- -## Troubleshooting {/*troubleshooting*/} +## Solución de problemas {/*troubleshooting*/} -### My Effect runs twice when the component mounts {/*my-effect-runs-twice-when-the-component-mounts*/} +### Mi efecto se ejecuta dos veces cuando el componente se monta {/*my-effect-runs-twice-when-the-component-mounts*/} -When Strict Mode is on, in development, React runs setup and cleanup one extra time before the actual setup. +Cuando el modo estricto está activado, en el desarrollo, React ejecuta la configuración y la limpieza una vez más antes de la configuración real. -This is a stress-test that verifies your Effect’s logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn’t be able to distinguish between the setup being called once (as in production) and a setup → cleanup → setup sequence (as in development). +Esta es una prueba de estrés que verifica que la lógica de su efecto se implementa correctamente. Si esto causa problemas visibles, probablemente tengas que echarle un ojo a tu función de limpieza. La función de limpieza debe detener o deshacer lo que la función de configuración estaba haciendo. La regla general es que el usuario no debería ser capaz de distinguir entre la configuración que se llama una vez (como en producción) y una secuencia de configuración → limpieza → configuración (como en desarrollo). -Read more about [how this helps find bugs](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) and [how to fix your logic.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +Lee más sobre [cómo esto ayuda a encontrar errores](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) y [cómo arreglar tu lógica.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) --- -### My Effect runs after every re-render {/*my-effect-runs-after-every-re-render*/} +### Mi efecto se ejecuta después de cada re-renderización {/*my-effect-runs-after-every-re-render*/} -First, check that you haven't forgotten to specify the dependency array: +En primer lugar, comprueba que no has olvidado especificar el _array_ de dependencias: ```js {3} useEffect(() => { // ... -}); // 🚩 No dependency array: re-runs after every render! +}); // 🚩 No hay array de dependencias: ¡se vuelve a ejecutar después de cada renderización! ``` -If you've specified the dependency array but your Effect still re-runs in a loop, it's because one of your dependencies is different on every re-render. +Si has especificado el _array_ de dependencias, pero tu Efecto aún se vuelve a ejecutar en un bucle, es porque una de tus dependencias es diferente en cada re-renderización. -You can debug this problem by manually logging your dependencies to the console: +Puedes depurar este problema imprimiendo manualmente tus dependencias en la consola: ```js {5} useEffect(() => { @@ -1787,58 +1791,58 @@ You can debug this problem by manually logging your dependencies to the console: console.log([serverUrl, roomId]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +A continuación, puedes hacer clic con el botón derecho del ratón en los _arrays_ de las diferentes renderizaciones en la consola y seleccionar "Guardar como variable global" para ambas. Suponiendo que la primera se guardó como `temp1` y la segunda se guardó como `temp2`, entonces puedes usar la consola del navegador para comprobar si cada dependencia en ambos array es la misma: ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // ¿La primera dependencia es la misma entre los arrays? +Object.is(temp1[1], temp2[1]); // ¿La segunda dependencia es la misma entre los arrays? +Object.is(temp1[2], temp2[2]); // ... y así sucesivamente para cada dependencia ... ``` -When you find the dependency that is different on every re-render, you can usually fix it in one of these ways: +Cuando encuentres la dependencia que es diferente en cada renderización, normalmente puedes arreglarlo de una de estas maneras: -- [Updating state based on previous state from an Effect](#updating-state-based-on-previous-state-from-an-effect) -- [Removing unnecessary object dependencies](#removing-unnecessary-object-dependencies) -- [Removing unnecessary function dependencies](#removing-unnecessary-function-dependencies) -- [Reading the latest props and state from an Effect](#reading-the-latest-props-and-state-from-an-effect) +- [Actualización del estado basado en el estado anterior de un efecto](#updating-state-based-on-previous-state-from-an-effect) +- [Eliminación de dependencias de objetos innecesarias](#removing-unnecessary-object-dependencies) +- [Eliminación de dependencias de funciones innecesarias](#removing-unnecessary-function-dependencies) +- [Lectura de las últimas props y estados de un efecto](#reading-the-latest-props-and-state-from-an-effect) -As a last resort (if these methods didn't help), wrap its creation with [`useMemo`](/apis/react/useMemo#memoizing-a-dependency-of-another-hook) or [`useCallback`](/apis/react/useCallback#preventing-an-effect-from-firing-too-often) (for functions). +Como último recurso (si estos métodos no ayudan) [envuelve el valor con `useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) o, en funciones, [con `useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) (para funciones). --- -### My Effect keeps re-running in an infinite cycle {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} +### Mi efecto se repite en un ciclo infinito {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} -If your Effect runs in an infinite cycle, these two things must be true: +Si tu Efecto se ejecuta en un ciclo infinito, estas dos cosas deben estar ocurriendo: -- Your Effect is updating some state. -- That state leads to a re-render, which causes the Effect's dependencies to change. +- Tu efecto está actualizando algún estado. +- Ese estado provoca a una re-renderización, lo que hace que las dependencias del Efecto cambien. -Before you start fixing the problem, ask yourself whether your Effect is connecting to some external system (like DOM, network, a third-party widget, and so on). Why does your Effect need to set state? Does it synchronize some state with that external system? Or are you trying to manage your application's data flow with it? +Antes de empezar a solucionar el problema, pregúntate si tu efecto se está conectando a algún sistema externo (como el DOM, la red, un widget de terceros, etc.). ¿Por qué tu efecto necesita establecer un estado? ¿Sincroniza algún estado con ese sistema externo? ¿O estás intentando gestionar el flujo de datos de tu aplicación con él? -If there is no external system, consider whether [removing the Effect altogether](/learn/you-might-not-need-an-effect) would simplify your logic. +Si no hay un sistema externo, considera si la [eliminación del Efecto por completo](/learn/you-might-not-need-an-effect) simplificaría su lógica. -If you're genuinely synchronizing with some external system, think about why and under what conditions your Effect should update the state. Has something changed that affects your component's visual output? If you need to keep track of some data that isn't used by rendering, a [ref](/apis/react/useRef#referencing-a-value-with-a-ref) (which doesn't trigger re-renders) might be more appropriate. Verify your Effect doesn't update the state (and trigger re-renders) more than needed. +Si realmente estás sincronizando con algún sistema externo, piensa por qué y bajo qué condiciones tu Efecto debe actualizar el estado. ¿Ha cambiado algo que afecta a la salida visual de tu componente? Si necesitas hacer un seguimiento de algunos datos que no son utilizados por la renderización, una [ref](/reference/react/useRef#referencing-a-value-with-a-ref) (que no desencadena la re-renderización) podría ser más apropiada. Comprueba que tu efecto no actualiza el estado (y no provoca la re-renderización) más de lo necesario. -Finally, if your Effect is updating the state at the right time, but there is still a loop, it's because that state update leads to one of your Effect's dependencies changing. [Read how to debug and resolve dependency changes.](/apis/react/useEffect#my-effect-runs-after-every-re-render) +Por último, si tu efecto está actualizando el estado en el momento adecuado, pero sigue habiendo un bucle, es porque esa actualización de estado hace que cambie una de las dependencias de tu efecto. [Lee cómo depurar y resolver los cambios de dependencias.](/reference/react/useEffect#my-effect-runs-after-every-re-render) --- -### My cleanup logic runs even though my component didn't unmount {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} +### Mi lógica de limpieza se ejecuta a pesar de que mi componente no se ha desmontado {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} -The cleanup function runs not only during unmount, but before every re-render with changed dependencies. Additionally, in development, React [runs setup+cleanup one extra time immediately after component mounts.](#my-effect-runs-twice-when-the-component-mounts) +La función de limpieza se ejecuta no sólo durante el desmontaje, sino antes de cada renderización con dependencias cambiadas. Además, en el desarrollo, React [ejecuta una configuración y limpieza una vez más inmediatamente después de montar el componente.](#my-effect-runs-twice-when-the-component-mounts) -If you have cleanup code without corresponding setup code, it's usually a code smell: +Si tienes código de limpieza sin el correspondiente código de configuración, suele ser un error de código: ```js {2-5} useEffect(() => { - // 🔴 Avoid: Cleanup logic without corresponding setup logic + // 🔴 Avoid: Lógica de limpieza sin la correspondiente lógica de configuración return () => { doSomething(); }; }, []); ``` -Your cleanup logic should be "symmetrical" to the setup logic, and should stop or undo whatever setup did: +Tu lógica de limpieza debería ser "simétrica" a la lógica de configuración, y debe detener o deshacer lo que hizo la configuración: ```js {2-3,5} useEffect(() => { @@ -1850,10 +1854,10 @@ Your cleanup logic should be "symmetrical" to the setup logic, and should stop o }, [serverUrl, roomId]); ``` -[Learn how the Effect lifecycle is different from the component's lifecycle.](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) +[Aprende cómo el ciclo de vida del efecto es diferente del ciclo de vida del componente.](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) --- -### My Effect does something visual, and I see a flicker before it runs {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} +### Mi efecto hace algo visual, y veo un parpadeo antes de que se ejecute {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} -If your Effect must block the browser from [painting the screen,](/learn/render-and-commit#epilogue-browser-paint) replace `useEffect` with [`useLayoutEffect`](/apis/react/useLayoutEffect). Note that **this shouldn't be needed for the vast majority of Effects.** You'll only need this if it's crucial to run your Effect before the browser paint: for example, to measure and position a tooltip before the user sees it for the first time. +Si tu efecto debe evitar que el navegador [pinte la pantalla,](/learn/render-and-commit#epilogue-browser-paint) sustituye `useEffect` por [`useLayoutEffect`](/reference/react/useLayoutEffect). Ten en cuenta que esto **no debería ser necesario para la gran mayoría de los Efectos.** Sólo lo necesitarás si es crucial ejecutar tu Efecto antes de que el navegador pinte la pantalla: por ejemplo, para medir y posicionar un _tooltip_ antes de que el usuario lo vea por primera vez.