From 86f60169c5f72821156b02b0be553bd108dceb59 Mon Sep 17 00:00:00 2001 From: Anthoni Broering dos Santos Date: Mon, 26 Aug 2024 01:27:46 -0300 Subject: [PATCH 1/2] feat: translate half of the page on Effect dependencies - Translated the section on handling effect dependencies in React. - Improved clarity of explanations and examples. - Added guidelines to avoid common bugs and best practices. Next steps: complete the translation of the page and review the content. --- .../learn/removing-effect-dependencies.md | 446 +++++++++--------- 1 file changed, 223 insertions(+), 223 deletions(-) diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 9a871c6c3..2ccbebb44 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -1,26 +1,26 @@ --- -title: 'Removing Effect Dependencies' +title: 'Removendo Dependências de Efeitos' --- -When you write an Effect, the linter will verify that you've included every reactive value (like props and state) that the Effect reads in the list of your Effect's dependencies. This ensures that your Effect remains synchronized with the latest props and state of your component. Unnecessary dependencies may cause your Effect to run too often, or even create an infinite loop. Follow this guide to review and remove unnecessary dependencies from your Effects. +Quando você escreve um efeito, o linter verifica se você incluiu todos os valores reativos (como props e state) que o efeito lê na lista de dependências do seu efeito. Isso garante que seu efeito permaneça sincronizado com as últimas propriedades e o estado de seu componente. Dependências desnecessárias podem fazer com que seu Efeito seja executado com muita frequência ou até mesmo criar um loop infinito. Siga este guia para revisar e remover dependências desnecessárias de seus efeitos. -- How to fix infinite Effect dependency loops -- What to do when you want to remove a dependency -- How to read a value from your Effect without "reacting" to it -- How and why to avoid object and function dependencies -- Why suppressing the dependency linter is dangerous, and what to do instead +- Como corrigir loops de dependência de efeito infinito +- O que fazer quando você quiser remover uma dependência +- Como ler um valor de seu Effect sem “reagir” a ele +- Como e por que evitar dependências de objetos e funções +- Por que suprimir o linter de dependência é perigoso e o que fazer em vez disso -## Dependencies should match the code {/*dependencies-should-match-the-code*/} +## As Dependências devem corresponder ao código {/*dependencies-should-match-the-code*/} -When you write an Effect, you first specify how to [start and stop](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) whatever you want your Effect to be doing: +Quando você escreve um Efeito, o primeiro passo é especifica como [iniciar e parar](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) o que o seu efeito faz: ```js {5-7} const serverUrl = 'https://localhost:1234'; @@ -30,11 +30,11 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - // ... + // ... } ``` -Then, if you leave the Effect dependencies empty (`[]`), the linter will suggest the correct dependencies: +Portanto, se você deixar as dependências do Efeito vazias (`[]`), o linter sugerirá as dependências corretas: @@ -49,23 +49,23 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // <-- Fix the mistake here! - return

Welcome to the {roomId} room!

; + }, []); // <-- Corrija o erro aqui! + return

Bem-vindo(a) à sala {roomId}!

; } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); return ( <>
@@ -77,13 +77,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Uma implementação real se conectaria ao servidor. return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando-se à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -96,7 +96,7 @@ button { margin-left: 10px; }
-Fill them in according to what the linter says: +Preencha-os de acordo com o que o linter indica: ```js {6} function ChatRoom({ roomId }) { @@ -104,12 +104,12 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... } ``` -[Effects "react" to reactive values.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Since `roomId` is a reactive value (it can change due to a re-render), the linter verifies that you've specified it as a dependency. If `roomId` receives a different value, React will re-synchronize your Effect. This ensures that the chat stays connected to the selected room and "reacts" to the dropdown: +Os [Efeitos "reagem" a valores reativos.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Como `roomId` é um valor reativo (ele pode mudar devido a uma re-renderização), o linter verifica se você o especificou como uma dependência. Se `roomId` receber um valor diferente, o React irá re-sincronizar o seu Efeito. Isso garante que o chat permaneça conectado à sala selecionada e "reaja" ao menu suspenso: @@ -125,22 +125,22 @@ function ChatRoom({ roomId }) { connection.connect(); return () => connection.disconnect(); }, [roomId]); - return

Welcome to the {roomId} room!

; + return

Bem-vindo(a) à sala {roomId}!

; } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); return ( <>
@@ -152,13 +152,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Uma implementação real se conectaria ao servidor. return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando-se à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -171,24 +171,24 @@ button { margin-left: 10px; }
-### To remove a dependency, prove that it's not a dependency {/*to-remove-a-dependency-prove-that-its-not-a-dependency*/} +### Para remover uma dependência, prove que ela não é uma dependência {/*to-remove-a-dependency-prove-that-its-not-a-dependency*/} -Notice that you can't "choose" the dependencies of your Effect. Every reactive value used by your Effect's code must be declared in your dependency list. The dependency list is determined by the surrounding code: +Observe que você não pode "escolher" as dependências do seu Efeito. Todo valor reativo usado pelo código do seu Efeito deve ser declarado na sua lista de dependências. A lista de dependências é estabelecida pelo código ao redor: ```js [[2, 3, "roomId"], [2, 5, "roomId"], [2, 8, "roomId"]] const serverUrl = 'https://localhost:1234'; -function ChatRoom({ roomId }) { // This is a reactive value +function ChatRoom({ roomId }) { // Este é um valor reativo useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads that reactive value + const connection = createConnection(serverUrl, roomId); // Esse efeito indica que o valor reativo connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ So you must specify that reactive value as a dependency of your Effect + }, [roomId]); // ✅ Portanto, você deve especificar esse valor reativo como uma dependência do seu Efeito // ... } ``` -[Reactive values](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive) include props and all variables and functions declared directly inside of your component. Since `roomId` is a reactive value, you can't remove it from the dependency list. The linter wouldn't allow it: +[Valores reativos](/learn/lifecycle-of-reactive-effects#all-variables-declared-in-the-component-body-are-reactive) incluem *props* e todas as variáveis e funções declaradas diretamente dentro do seu componente. Como `roomId` é um valor reativo, você não pode removê-lo da lista de dependências. O linter não permitirá isso: ```js {8} const serverUrl = 'https://localhost:1234'; @@ -198,30 +198,30 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // 🔴 React Hook useEffect has a missing dependency: 'roomId' + }, []); // 🔴 React Hook useEffect tem uma dependência ausente: 'roomId' // ... } ``` -And the linter would be right! Since `roomId` may change over time, this would introduce a bug in your code. +E o linter estaria correto! Como o `roomId` pode mudar ao longo do tempo, removê-lo da lista de dependências poderia introduzir um bug no seu código. -**To remove a dependency, "prove" to the linter that it *doesn't need* to be a dependency.** For example, you can move `roomId` out of your component to prove that it's not reactive and won't change on re-renders: +**Para remover uma dependência, "prove" ao linter que ela *não precisa* ser uma dependência**. Por exemplo, você pode mover `roomId` para fora do seu componente para demonstrar que ele não é reativo e não mudará em re-renderizações: ```js {2,9} const serverUrl = 'https://localhost:1234'; -const roomId = 'music'; // Not a reactive value anymore +const roomId = 'música'; // Não se trata mais de um valor reativo function ChatRoom() { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Todas as dependências declaradas // ... } ``` -Now that `roomId` is not a reactive value (and can't change on a re-render), it doesn't need to be a dependency: +Agora que `roomId` não é um valor reativo (e não pode ser alterado em uma nova renderização), ele não precisa ser uma dependência: @@ -230,7 +230,7 @@ import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; const serverUrl = 'https://localhost:1234'; -const roomId = 'music'; +const roomId = 'música'; export default function ChatRoom() { useEffect(() => { @@ -238,19 +238,19 @@ export default function ChatRoom() { connection.connect(); return () => connection.disconnect(); }, []); - return

Welcome to the {roomId} room!

; + return

Bem-vindo(a) à sala {roomId}!

; } ``` ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Uma implementação real se conectaria ao servidor. return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando-se à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -263,43 +263,43 @@ button { margin-left: 10px; }
-This is why you could now specify an [empty (`[]`) dependency list.](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) Your Effect *really doesn't* depend on any reactive value anymore, so it *really doesn't* need to re-run when any of the component's props or state change. +É por isso que agora você pode especificar uma [lista de dependências vazia (`[]`).](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) Seu Efeito *não depende* mais de nenhum valor reativo, portanto, *não precisa* ser reexecutado quando qualquer propriedade ou estado do componente for alterado. -### To change the dependencies, change the code {/*to-change-the-dependencies-change-the-code*/} +### Para alterar as dependências, altere o código {/*to-change-the-dependencies-change-the-code*/} -You might have noticed a pattern in your workflow: +Talvez você tenha notado um padrão em seu fluxo de trabalho: -1. First, you **change the code** of your Effect or how your reactive values are declared. -2. Then, you follow the linter and adjust the dependencies to **match the code you have changed.** -3. If you're not happy with the list of dependencies, you **go back to the first step** (and change the code again). +1. Primeiro, você **altera o código** do seu Efeito ou a forma como seus valores reativos são declarados. +2. Em seguida, você segue as orientações do linter e ajusta as dependências para **corresponder ao código que você alterou**. +3. Se não estiver satisfeito com a lista de dependências, você **volta ao primeiro passo** (e altera o código novamente). -The last part is important. **If you want to change the dependencies, change the surrounding code first.** You can think of the dependency list as [a list of all the reactive values used by your Effect's code.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) You don't *choose* what to put on that list. The list *describes* your code. To change the dependency list, change the code. +A última parte é importante. **Se você deseja alterar as dependências, modifique o código ao redor primeiro.** Você pode considerar a lista de dependências como [uma lista de todos os valores reativos usados pelo código do seu Effect.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Você não *escolhe* o que colocar nessa lista. A lista *descreve* o seu código. Para alterar a lista de dependências, altere o código. -This might feel like solving an equation. You might start with a goal (for example, to remove a dependency), and you need to "find" the code matching that goal. Not everyone finds solving equations fun, and the same thing could be said about writing Effects! Luckily, there is a list of common recipes that you can try below. +Isso pode parecer como resolver uma equação. Você pode começar com um objetivo (por exemplo, remover uma dependência) e precisa “encontrar” o código que corresponda a esse objetivo. Nem todo mundo acha divertido resolver equações, e a mesma coisa pode ser dita sobre escrever efeitos! Felizmente, há uma lista de receitas comuns que você pode experimentar abaixo. -If you have an existing codebase, you might have some Effects that suppress the linter like this: +Se você tiver uma base de código existente, poderá ter alguns efeitos que suprimem o linter dessa forma: ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 Evite ignorar o linter assim: // eslint-ignore-next-line react-hooks/exhaustive-deps }, []); ``` -**When dependencies don't match the code, there is a very high risk of introducing bugs.** By suppressing the linter, you "lie" to React about the values your Effect depends on. +**Quando as dependências não correspondem ao código, há um risco muito alto de introduzir bugs.** Ignorar o linter faz com que você “enganar” o React sobre os valores dos quais seu efeito depende. -Instead, use the techniques below. +Em vez disso, utilize as técnicas abaixo. -#### Why is suppressing the dependency linter so dangerous? {/*why-is-suppressing-the-dependency-linter-so-dangerous*/} +#### Por que ignorar o linter de dependências é tão perigoso? {/*why-is-suppressing-the-dependency-linter-so-dangerous*/} -Suppressing the linter leads to very unintuitive bugs that are hard to find and fix. Here's one example: +Ignorar o linter pode resultar em bugs complexos e não intuitivos, que são difíceis de identificar e corrigir. Veja um exemplo: @@ -311,7 +311,7 @@ export default function Timer() { const [increment, setIncrement] = useState(1); function onTick() { - setCount(count + increment); + setCount(count + increment); } useEffect(() => { @@ -323,12 +323,12 @@ export default function Timer() { return ( <>

- Counter: {count} - + Contador: {count} +


- Every second, increment by: + A cada segundo, incremente: @@ -348,31 +348,31 @@ button { margin: 10px; } -Let's say that you wanted to run the Effect "only on mount". You've read that [empty (`[]`) dependencies](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) do that, so you've decided to ignore the linter, and forcefully specified `[]` as the dependencies. +Digamos que você queira executar o efeito “somente na montagem”. Você leu que uma lista de dependências [vazia (`[]`)](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) faz isso, então decidiu ignorar o linter e especificou `[]` como as dependências. -This counter was supposed to increment every second by the amount configurable with the two buttons. However, since you "lied" to React that this Effect doesn't depend on anything, React forever keeps using the `onTick` function from the initial render. [During that render,](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time) `count` was `0` and `increment` was `1`. This is why `onTick` from that render always calls `setCount(0 + 1)` every second, and you always see `1`. Bugs like this are harder to fix when they're spread across multiple components. +Este contador deveria ser incrementado a cada segundo pelo valor configurável com os dois botões. No entanto, como você “enganou” o React, dizendo que esse Efeito não depende de nada, o React continua usando a função onTick do render inicial. [Durante esse render](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), o `count` era `0` e o `increment` era `1`. É por isso que `onTick` daquele render sempre chama `setCount(0 + 1)` a cada segundo, e você sempre vê o valor `1`. Bugs como este são mais difíceis de corrigir quando estão espalhados por vários componentes. -There's always a better solution than ignoring the linter! To fix this code, you need to add `onTick` to the dependency list. (To ensure the interval is only setup once, [make `onTick` an Effect Event.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)) +Sempre há uma solução melhor do que ignorar o linter! Para corrigir esse código, você precisa adicionar `onTick` à lista de dependências. (Para garantir que o intervalo seja configurado apenas uma vez, [faça `onTick` um Evento de Efeito](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events)) -**We recommend treating the dependency lint error as a compilation error. If you don't suppress it, you will never see bugs like this.** The rest of this page documents the alternatives for this and other cases. +**Recomendamos tratar o erro de dependência do lint como um erro de compilação. Se você não ignorá-lo, nunca encontrará erros como este.** O restante desta página documenta as alternativas para esse e outros casos. -## Removing unnecessary dependencies {/*removing-unnecessary-dependencies*/} +## Removendo dependências desnecessárias {/*removing-unnecessary-dependencies*/} -Every time you adjust the Effect's dependencies to reflect the code, look at the dependency list. Does it make sense for the Effect to re-run when any of these dependencies change? Sometimes, the answer is "no": +Toda vez que você ajustar as dependências do Efeito para refletir o código, examine a lista de dependências. Faz sentido que o Efeito seja executado novamente quando alguma dessas dependências for alterada? Às vezes, a resposta é “não”: -* You might want to re-execute *different parts* of your Effect under different conditions. -* You might want to only read the *latest value* of some dependency instead of "reacting" to its changes. -* A dependency may change too often *unintentionally* because it's an object or a function. +- Você pode querer reexecutar *partes diferentes* do seu Efeito sob condições distintas. +- Pode ser necessário ler apenas o *valor mais recente* de alguma dependência em vez de "reagir" às suas alterações. +- Uma dependência pode mudar com muita frequência *não intencionalmente* porque é um objeto ou uma função. -To find the right solution, you'll need to answer a few questions about your Effect. Let's walk through them. +Para encontrar a solução certa, você precisará responder a algumas perguntas sobre o seu Efeito. Vamos examiná-las. -### Should this code move to an event handler? {/*should-this-code-move-to-an-event-handler*/} +### Esse código deve ser movido para um manipulador de eventos? {/*should-this-code-move-to-an-event-handler*/} -The first thing you should think about is whether this code should be an Effect at all. +A primeira coisa que você deve pensar é se esse código deve ser um Efeito. -Imagine a form. On submit, you set the `submitted` state variable to `true`. You need to send a POST request and show a notification. You've put this logic inside an Effect that "reacts" to `submitted` being `true`: +Imagine um formulário. Ao enviar, você define a variável de estado `submitted` como `true`. Você precisa enviar uma solicitação POST e mostrar uma notificação. Você colocou essa lógica dentro de um Efeito que “reage” ao fato de `submitted` ser `true`: ```js {6-8} function Form() { @@ -380,9 +380,9 @@ function Form() { useEffect(() => { if (submitted) { - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Evite: Lógica específica do evento dentro de um Efeito post('/api/register'); - showNotification('Successfully registered!'); + showNotification('Bem-sucedido registrado!'); } }, [submitted]); @@ -394,7 +394,7 @@ function Form() { } ``` -Later, you want to style the notification message according to the current theme, so you read the current theme. Since `theme` is declared in the component body, it is a reactive value, so you add it as a dependency: +Posteriormente, você deseja estilizar a mensagem de notificação de acordo com o tema atual, portanto, você lê o tema atual. Como o `theme` é declarado no corpo do componente, ele é um valor reativo, portanto, você o adiciona como uma dependência: ```js {3,9,11} function Form() { @@ -403,11 +403,11 @@ function Form() { useEffect(() => { if (submitted) { - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Evite: Lógica específica do evento dentro de um Efeito post('/api/register'); - showNotification('Successfully registered!', theme); + showNotification('Bem-sucedido registrado!', theme); } - }, [submitted, theme]); // ✅ All dependencies declared + }, [submitted, theme]); // ✅ Todas as dependências declaradas function handleSubmit() { setSubmitted(true); @@ -417,31 +417,31 @@ function Form() { } ``` -By doing this, you've introduced a bug. Imagine you submit the form first and then switch between Dark and Light themes. The `theme` will change, the Effect will re-run, and so it will display the same notification again! +Ao fazer isso, você introduziu um bug. Imagine que você envie o formulário primeiro e depois alterne entre os temas Escuro e Claro. O `theme` mudará, o Efeito será executado novamente e, portanto, exibirá a mesma notificação novamente! -**The problem here is that this shouldn't be an Effect in the first place.** You want to send this POST request and show the notification in response to *submitting the form,* which is a particular interaction. To run some code in response to particular interaction, put that logic directly into the corresponding event handler: +**O problema aqui é que isso não deveria ser um efeito em primeiro lugar.** Você deseja enviar essa solicitação POST e mostrar a notificação em resposta ao *envio do formulário*, que é uma interação específica. Para executar algum código em resposta a uma interação específica, coloque essa lógica diretamente no manipulador de eventos correspondente: ```js {6-7} function Form() { const theme = useContext(ThemeContext); function handleSubmit() { - // ✅ Good: Event-specific logic is called from event handlers + // ✅ Bom: A lógica específica do evento é chamada pelos manipuladores de eventos post('/api/register'); - showNotification('Successfully registered!', theme); + showNotification('Bem-sucedido registrado!', theme); } // ... } ``` -Now that the code is in an event handler, it's not reactive--so it will only run when the user submits the form. Read more about [choosing between event handlers and Effects](/learn/separating-events-from-effects#reactive-values-and-reactive-logic) and [how to delete unnecessary Effects.](/learn/you-might-not-need-an-effect) +Agora que o código está em um manipulador de eventos, ele não é reativo, portanto, só será executado quando o usuário enviar o formulário. Leia mais sobre [escolhendo entre manipuladores de eventos e efeitos](/learn/separating-events-from-effects#reactive-values-and-reactive-logic) e [como excluir efeitos desnecessários](/learn/you-might-not-need-an-effect) -### Is your Effect doing several unrelated things? {/*is-your-effect-doing-several-unrelated-things*/} +### Seu efeito está fazendo várias coisas não relacionadas? {/*is-your-effect-doing-several-unrelated-things*/} -The next question you should ask yourself is whether your Effect is doing several unrelated things. +A próxima pergunta que você deve fazer a si mesmo é se o seu Efeito está fazendo várias coisas não relacionadas. -Imagine you're creating a shipping form where the user needs to choose their city and area. You fetch the list of `cities` from the server according to the selected `country` to show them in a dropdown: +Imagine que você está criando um formulário de envio em que o usuário precisa escolher a cidade e a região. Você obtém a lista de `cities` do servidor de acordo com o `country` selecionado para mostrá-las em um menu suspenso: ```js function ShippingForm({ country }) { @@ -460,14 +460,14 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country]); // ✅ All dependencies declared + }, [country]); // ✅ Todas as dependências declaradas // ... ``` -This is a good example of [fetching data in an Effect.](/learn/you-might-not-need-an-effect#fetching-data) You are synchronizing the `cities` state with the network according to the `country` prop. You can't do this in an event handler because you need to fetch as soon as `ShippingForm` is displayed and whenever the `country` changes (no matter which interaction causes it). +Esse é um bom exemplo de [obtenção de dados em um Efeito](/learn/you-might-not-need-an-effect#fetching-data) Você está sincronizando o estado das `cities` com a rede de acordo com a propriedade `country`. Não é possível fazer isso em um manipulador de eventos porque você precisa buscar os dados assim que o `ShippingForm` for exibido e sempre que o `country` for alterado (independentemente da interação que o causar). -Now let's say you're adding a second select box for city areas, which should fetch the `areas` for the currently selected `city`. You might start by adding a second `fetch` call for the list of areas inside the same Effect: +Agora, digamos que você está adicionando uma segunda caixa de seleção para áreas da cidade, que deve buscar as `areas` da `city` selecionada no momento. Você pode começar adicionando uma segunda chamada `fetch` para obter a lista de áreas dentro do mesmo Efeito: ```js {15-24,28} function ShippingForm({ country }) { @@ -484,7 +484,7 @@ function ShippingForm({ country }) { setCities(json); } }); - // 🔴 Avoid: A single Effect synchronizes two independent processes + // 🔴 Evite: Um único efeito sincroniza dois processos independentes if (city) { fetch(`/api/areas?city=${city}`) .then(response => response.json()) @@ -497,19 +497,19 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country, city]); // ✅ All dependencies declared + }, [country, city]); // ✅ Todas as dependências declaradas // ... ``` -However, since the Effect now uses the `city` state variable, you've had to add `city` to the list of dependencies. That, in turn, introduced a problem: when the user selects a different city, the Effect will re-run and call `fetchCities(country)`. As a result, you will be unnecessarily refetching the list of cities many times. +Entretanto, como o Efeito agora usa a variável de estado `city`, você teve que adicionar `city` à lista de dependências. Isso, por sua vez, introduziu um problema: quando o usuário selecionar uma cidade diferente, o Efeito será executado novamente e chamará `fetchCities(country)`. Como resultado, você estará recuperando desnecessariamente a lista de cidades várias vezes. -**The problem with this code is that you're synchronizing two different unrelated things:** +**O problema com esse código é que você está sincronizando duas coisas diferentes e não relacionadas:** -1. You want to synchronize the `cities` state to the network based on the `country` prop. -1. You want to synchronize the `areas` state to the network based on the `city` state. +1. Você deseja sincronizar o estado `cities` com a rede com base na propriedade `country`. +1. Você deseja sincronizar o estado `areas` com a rede com base no estado `city`. -Split the logic into two Effects, each of which reacts to the prop that it needs to synchronize with: +Divida a lógica em dois Efeitos, cada um dos quais reage à propriedade com a qual precisa se sincronizar: ```js {19-33} function ShippingForm({ country }) { @@ -526,7 +526,7 @@ function ShippingForm({ country }) { return () => { ignore = true; }; - }, [country]); // ✅ All dependencies declared + }, [country]); // ✅ Todas as dependências declaradas const [city, setCity] = useState(null); const [areas, setAreas] = useState(null); @@ -544,18 +544,18 @@ function ShippingForm({ country }) { ignore = true; }; } - }, [city]); // ✅ All dependencies declared + }, [city]); // ✅ Todas as dependências declaradas // ... ``` -Now the first Effect only re-runs if the `country` changes, while the second Effect re-runs when the `city` changes. You've separated them by purpose: two different things are synchronized by two separate Effects. Two separate Effects have two separate dependency lists, so they won't trigger each other unintentionally. +Agora, o primeiro efeito só é executado novamente se o `country` altera, enquanto o segundo efeito é executado novamente quando a `city` altera. Você os separou por finalidade: duas coisas diferentes são sincronizadas por dois efeitos separados. Dois efeitos separados têm duas listas de dependências separadas, de modo que não acionam um ao outro de forma não intencional. -The final code is longer than the original, but splitting these Effects is still correct. [Each Effect should represent an independent synchronization process.](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) In this example, deleting one Effect doesn't break the other Effect's logic. This means they *synchronize different things,* and it's good to split them up. If you're concerned about duplication, you can improve this code by [extracting repetitive logic into a custom Hook.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) +O código final é mais longo que o original, mas a divisão desses Efeitos ainda está correta. [Cada efeito deve representar um processo de sincronização independente](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) Neste exemplo, a exclusão de um efeito não quebra a lógica do outro efeito. Isso significa que eles *sincronizam coisas diferentes* e é bom separá-los. Se estiver preocupado com a duplicação, você pode melhorar esse código [extraindo a lógica repetitiva em um Hook personalizado](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) -### Are you reading some state to calculate the next state? {/*are-you-reading-some-state-to-calculate-the-next-state*/} +### Você está lendo algum estado para calcular o próximo estado? {/*are-you-reading-some-state-to-calculate-the-next-state*/} -This Effect updates the `messages` state variable with a newly created array every time a new message arrives: +Esse Efeito atualiza a variável de estado `messages` com uma matriz recém-criada sempre que uma nova mensagem chega: ```js {2,6-8} function ChatRoom({ roomId }) { @@ -569,7 +569,7 @@ function ChatRoom({ roomId }) { // ... ``` -It uses the `messages` variable to [create a new array](/learn/updating-arrays-in-state) starting with all the existing messages and adds the new message at the end. However, since `messages` is a reactive value read by an Effect, it must be a dependency: +Ele usa a variável `messages` para [criar uma nova matriz](/learn/updating-arrays-in-state) começando com todas as mensagens existentes e adicionando a nova mensagem no final. Entretanto, como `messages` é um valor reativo lido por um Effect, ele deve ser uma dependência: ```js {7,10} function ChatRoom({ roomId }) { @@ -581,15 +581,15 @@ function ChatRoom({ roomId }) { setMessages([...messages, receivedMessage]); }); return () => connection.disconnect(); - }, [roomId, messages]); // ✅ All dependencies declared + }, [roomId, messages]); // ✅ Todas as dependências declaradas // ... ``` -And making `messages` a dependency introduces a problem. +E tornar `messages` uma dependência introduz um problema. -Every time you receive a message, `setMessages()` causes the component to re-render with a new `messages` array that includes the received message. However, since this Effect now depends on `messages`, this will *also* re-synchronize the Effect. So every new message will make the chat re-connect. The user would not like that! +Toda vez que você recebe uma mensagem, `setMessages()` faz com que o componente seja renderizado novamente com uma nova matriz `messages` que inclui a mensagem recebida. Entretanto, como esse Efeito agora depende de `messages`, isso *também* ressincronizará o Efeito. Portanto, cada nova mensagem fará com que o chat se reconecte. O usuário não gostaria disso! -To fix the issue, don't read `messages` inside the Effect. Instead, pass an [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) to `setMessages`: +Para corrigir o problema, não leia `messages` dentro do Efeito. Em vez disso, passe uma [função de atualização](/reference/react/useState#updating-state-based-on-the-previous-state) para `setMessages`: ```js {7,10} function ChatRoom({ roomId }) { @@ -601,21 +601,21 @@ function ChatRoom({ roomId }) { setMessages(msgs => [...msgs, receivedMessage]); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... ``` -**Notice how your Effect does not read the `messages` variable at all now.** You only need to pass an updater function like `msgs => [...msgs, receivedMessage]`. React [puts your updater function in a queue](/learn/queueing-a-series-of-state-updates) and will provide the `msgs` argument to it during the next render. This is why the Effect itself doesn't need to depend on `messages` anymore. As a result of this fix, receiving a chat message will no longer make the chat re-connect. +**Observe como seu Efeito não lê a variável `messages` agora.** Você só precisa passar uma função atualizadora como `msgs => [...msgs, receivedMessage]`. O React [coloca sua função atualizadora em uma fila](/learn/queueing-a-series-of-state-updates) e fornecerá o argumento `msgs` a ela durante a próxima renderização. É por isso que o efeito em si não precisa mais depender de `messages`. Como resultado dessa correção, o recebimento de uma mensagem de chat não fará mais com que o chat seja reconectado. -### Do you want to read a value without "reacting" to its changes? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} +### Você deseja ler um valor sem “reagir” às suas alterações? {/*do-you-want-to-read-a-value-without-reacting-to-its-changes*/} -This section describes an **experimental API that has not yet been released** in a stable version of React. +Esta seção descreve uma API **experimental que ainda não foi lançada** em uma versão estável do React. -Suppose that you want to play a sound when the user receives a new message unless `isMuted` is `true`: +Suponha que você queira tocar um som quando o usuário receber uma nova mensagem, a menos que `isMuted` seja `true`: ```js {3,10-12} function ChatRoom({ roomId }) { @@ -634,7 +634,7 @@ function ChatRoom({ roomId }) { // ... ``` -Since your Effect now uses `isMuted` in its code, you have to add it to the dependencies: +Como seu Efeito agora usa `isMuted` em seu código, você precisa adicioná-lo às dependências: ```js {10,15} function ChatRoom({ roomId }) { @@ -651,13 +651,13 @@ function ChatRoom({ roomId }) { } }); return () => connection.disconnect(); - }, [roomId, isMuted]); // ✅ All dependencies declared + }, [roomId, isMuted]); // ✅ Todas as dependências declaradas // ... ``` -The problem is that every time `isMuted` changes (for example, when the user presses the "Muted" toggle), the Effect will re-synchronize, and reconnect to the chat. This is not the desired user experience! (In this example, even disabling the linter would not work--if you do that, `isMuted` would get "stuck" with its old value.) +O problema é que toda vez que `isMuted` é alterado (por exemplo, quando o usuário pressiona o botão de alternância Silenciado), o Efeito é ressincronizado e se reconecta ao chat. Essa não é a experiência de usuário desejada! (Neste exemplo, nem mesmo a desativação do linter funcionaria - se você fizer isso, o `isMuted` ficará “preso” ao seu valor antigo). -To solve this problem, you need to extract the logic that shouldn't be reactive out of the Effect. You don't want this Effect to "react" to the changes in `isMuted`. [Move this non-reactive piece of logic into an Effect Event:](/learn/separating-events-from-effects#declaring-an-effect-event) +Para resolver esse problema, você precisa extrair do Efeito a lógica que não deve ser reativa. Você não quer que esse efeito “reaja” às alterações em `isMuted`. [Mova essa parte não reativa da lógica para um Evento de Efeito:](/learn/separating-events-from-effects#declaring-an-effect-event) ```js {1,7-12,18,21} import { useState, useEffect, useEffectEvent } from 'react'; @@ -680,15 +680,15 @@ function ChatRoom({ roomId }) { onMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... ``` -Effect Events let you split an Effect into reactive parts (which should "react" to reactive values like `roomId` and their changes) and non-reactive parts (which only read their latest values, like `onMessage` reads `isMuted`). **Now that you read `isMuted` inside an Effect Event, it doesn't need to be a dependency of your Effect.** As a result, the chat won't re-connect when you toggle the "Muted" setting on and off, solving the original issue! +Os Eventos de Efeito permitem que você divida um Efeito em partes reativas (que devem “reagir” a valores reativos como `roomId` e suas alterações) e partes não reativas (que apenas leem seus valores mais recentes, como `onMessage` lê `isMuted`). **Agora que você lê `isMuted` dentro de um Evento de Efeito, ele não precisa ser uma dependência do seu Efeito.** Como resultado, o chat não se reconectará quando você ativar e desativar a configuração “Muted”, resolvendo o problema original! -#### Wrapping an event handler from the props {/*wrapping-an-event-handler-from-the-props*/} +#### Envolvimento de um manipulador de eventos a partir dos props {/*wrapping-an-event-handler-from-the-props*/} -You might run into a similar problem when your component receives an event handler as a prop: +Você pode se deparar com um problema semelhante quando seu componente recebe um manipulador de eventos como uma propriedade: ```js {1,8,11} function ChatRoom({ roomId, onReceiveMessage }) { @@ -701,11 +701,11 @@ function ChatRoom({ roomId, onReceiveMessage }) { onReceiveMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId, onReceiveMessage]); // ✅ All dependencies declared + }, [roomId, onReceiveMessage]); // ✅ Todas as dependências declaradas // ... ``` -Suppose that the parent component passes a *different* `onReceiveMessage` function on every render: +Suponha que o componente pai passe uma função `onReceiveMessage` *diferente* a cada renderização: ```js {3-5} ``` -Since `onReceiveMessage` is a dependency, it would cause the Effect to re-synchronize after every parent re-render. This would make it re-connect to the chat. To solve this, wrap the call in an Effect Event: +Como o `onReceiveMessage` é uma dependência, isso faria com que o Effect fosse ressincronizado após cada nova renderização do pai. Isso faria com que ele se reconectasse ao chat. Para resolver isso, envolva a chamada em um Evento de Efeito: ```js {4-6,12,15} function ChatRoom({ roomId, onReceiveMessage }) { @@ -733,17 +733,17 @@ function ChatRoom({ roomId, onReceiveMessage }) { onMessage(receivedMessage); }); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... ``` -Effect Events aren't reactive, so you don't need to specify them as dependencies. As a result, the chat will no longer re-connect even if the parent component passes a function that's different on every re-render. +Os Eventos de Efeitos não são reativos, portanto, você não precisa especificá-los como dependências. Como resultado, o bate-papo não será mais reconectado, mesmo que o componente pai passe uma função que seja diferente a cada nova renderização. -#### Separating reactive and non-reactive code {/*separating-reactive-and-non-reactive-code*/} +#### Separação de código reativo e não reativo {/*separating-reactive-and-non-reactive-code*/} -In this example, you want to log a visit every time `roomId` changes. You want to include the current `notificationCount` with every log, but you *don't* want a change to `notificationCount` to trigger a log event. +Neste exemplo, você deseja registrar uma visita sempre que o `roomId` for alterado. Além disso, você quer incluir o `notificationCount` atual em cada registro, mas *sem* que uma alteração em `notificationCount` dispare um novo evento de registro. -The solution is again to split out the non-reactive code into an Effect Event: +A solução, mais uma vez, é separar o código não reativo em um Evento de Efeito: ```js {2-4,7} function Chat({ roomId, notificationCount }) { @@ -753,16 +753,16 @@ function Chat({ roomId, notificationCount }) { useEffect(() => { onVisit(roomId); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... } ``` -You want your logic to be reactive with regards to `roomId`, so you read `roomId` inside of your Effect. However, you don't want a change to `notificationCount` to log an extra visit, so you read `notificationCount` inside of the Effect Event. [Learn more about reading the latest props and state from Effects using Effect Events.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) +Você deseja que sua lógica seja reativa com relação a `roomId`, portanto, você lê `roomId` dentro do seu Efeito. No entanto, você não quer que uma alteração em `notificationCount` registre uma visita extra, então você lê `notificationCount` dentro do Evento de Efeito. [Saiba mais sobre como ler as últimas props e o estado dos efeitos usando Efeito Events](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) -### Does some reactive value change unintentionally? {/*does-some-reactive-value-change-unintentionally*/} +### Algum valor reativo muda involuntariamente? {/*does-some-reactive-value-change-unintentionally*/} -Sometimes, you *do* want your Effect to "react" to a certain value, but that value changes more often than you'd like--and might not reflect any actual change from the user's perspective. For example, let's say that you create an `options` object in the body of your component, and then read that object from inside of your Effect: +Às vezes, você *quer* que seu Efeito “reaja” a um determinado valor, mas esse valor muda com mais frequência do que você gostaria e pode não refletir nenhuma mudança real da perspectiva do usuário. Por exemplo, digamos que você crie um objeto `options` no corpo do seu componente e, em seguida, leia esse objeto de dentro do seu Efeito: ```js {3-6,9} function ChatRoom({ roomId }) { @@ -778,7 +778,7 @@ function ChatRoom({ roomId }) { // ... ``` -This object is declared in the component body, so it's a [reactive value.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) When you read a reactive value like this inside an Effect, you declare it as a dependency. This ensures your Effect "reacts" to its changes: +Esse objeto é declarado no corpo do componente, portanto, é um [valor reativo.](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) Quando você lê um valor reativo como esse dentro de um Efeito, você o declara como uma dependência. Isso garante que seu efeito “reaja” a suas alterações: ```js {3,6} // ... @@ -786,11 +786,11 @@ This object is declared in the component body, so it's a [reactive value.](/lear const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // ✅ All dependencies declared + }, [options]); // ✅ Todas as dependências declaradas // ... ``` -It is important to declare it as a dependency! This ensures, for example, that if the `roomId` changes, your Effect will re-connect to the chat with the new `options`. However, there is also a problem with the code above. To see it, try typing into the input in the sandbox below, and watch what happens in the console: +É importante declará-lo como uma dependência! Isso garante, por exemplo, que se o `roomId` for alterado, seu Efeito se conectará novamente ao chat com as novas `options`. No entanto, também há um problema com o código acima. Para ver isso, tente digitar na entrada da caixa de areia abaixo e observe o que acontece no console: @@ -803,7 +803,7 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - // Temporarily disable the linter to demonstrate the problem + // Desative temporariamente a linter para demonstrar o problema // eslint-disable-next-line react-hooks/exhaustive-deps const options = { serverUrl: serverUrl, @@ -818,25 +818,25 @@ function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bem-vindo(a) à sala {roomId}!

setMessage(e.target.value)} /> ); } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); return ( <>
@@ -847,14 +847,14 @@ export default function App() { ``` ```js src/chat.js -export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server +export function createConnection(serverUrl, roomId) { + // Uma implementação real se conectaria ao servidor. return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando-se à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -867,35 +867,35 @@ button { margin-left: 10px; }
-In the sandbox above, the input only updates the `message` state variable. From the user's perspective, this should not affect the chat connection. However, every time you update the `message`, your component re-renders. When your component re-renders, the code inside of it runs again from scratch. +No exemplo acima, a entrada de texto apenas atualiza a variável de estado `message`. Do ponto de vista do usuário, isso não deveria afetar a conexão do chat. No entanto, toda vez que você atualiza a `message`, o seu componente é re-renderizado. Quando o componente é re-renderizado, o código dentro dele é executado novamente desde o início. -A new `options` object is created from scratch on every re-render of the `ChatRoom` component. React sees that the `options` object is a *different object* from the `options` object created during the last render. This is why it re-synchronizes your Effect (which depends on `options`), and the chat re-connects as you type. +Um novo objeto `options` é criado do zero a cada re-renderização do componente ChatRoom. O React percebe que o objeto `options` é *diferente* do objeto `options` criado durante a renderização anterior. É por isso que ele re-sincroniza o seu Efeito (que depende de `options`), e o chat se reconecta conforme você digita. -**This problem only affects objects and functions. In JavaScript, each newly created object and function is considered distinct from all the others. It doesn't matter that the contents inside of them may be the same!** +**Esse problema afeta apenas objetos e funções. Em JavaScript, cada objeto e função recém-criados são considerados distintos de todos os outros. Não importa se o conteúdo dentro deles pode ser o mesmo!** ```js {7-8} -// During the first render -const options1 = { serverUrl: 'https://localhost:1234', roomId: 'music' }; +// Durante a primeira renderização +const options1 = { serverUrl: 'https://localhost:1234', roomId: 'música' }; -// During the next render -const options2 = { serverUrl: 'https://localhost:1234', roomId: 'music' }; +// Durante a próxima renderização +const options2 = { serverUrl: 'https://localhost:1234', roomId: 'música' }; -// These are two different objects! -console.log(Object.is(options1, options2)); // false +// Esses são dois objetos diferentes! +console.log(Object.is(options1, options2)); // falso ``` -**Object and function dependencies can make your Effect re-synchronize more often than you need.** +**As dependências de objetos e funções podem fazer com que seu Efeito seja ressincronizado com mais frequência do que o necessário.** -This is why, whenever possible, you should try to avoid objects and functions as your Effect's dependencies. Instead, try moving them outside the component, inside the Effect, or extracting primitive values out of them. +É por isso que, sempre que possível, você deve tentar evitar objetos e funções como dependências de seu Efeito. Em vez disso, tente movê-los para fora do componente, dentro do Efeito, ou extrair valores primitivos deles. -#### Move static objects and functions outside your component {/*move-static-objects-and-functions-outside-your-component*/} +#### Mova objetos e funções estáticas para fora do seu componente {/*move-static-objects-and-functions-outside-your-component*/} -If the object does not depend on any props and state, you can move that object outside your component: +Se o objeto não depender de nenhuma propriedade e estado, você poderá movê-lo para fora do seu componente: ```js {1-4,13} const options = { serverUrl: 'https://localhost:1234', - roomId: 'music' + roomId: 'música' }; function ChatRoom() { @@ -905,19 +905,19 @@ function ChatRoom() { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Todas as dependências declaradas // ... ``` -This way, you *prove* to the linter that it's not reactive. It can't change as a result of a re-render, so it doesn't need to be a dependency. Now re-rendering `ChatRoom` won't cause your Effect to re-synchronize. +Dessa forma, você *prova* para o linter que ele não é reativo. Como ele não pode mudar como resultado de uma re-renderização, não precisa ser uma dependência. Agora, re-renderizar `ChatRoom` não fará com que seu Efeito se re-sincronize. -This works for functions too: +Isso também funciona para funções: ```js {1-6,12} function createOptions() { return { serverUrl: 'https://localhost:1234', - roomId: 'music' + roomId: 'música' }; } @@ -929,15 +929,15 @@ function ChatRoom() { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Todas as dependências declaradas // ... ``` -Since `createOptions` is declared outside your component, it's not a reactive value. This is why it doesn't need to be specified in your Effect's dependencies, and why it won't ever cause your Effect to re-synchronize. +Como o `createOptions` é declarado fora de seu componente, ele não é um valor reativo. É por isso que ele não precisa ser especificado nas dependências de seu Effect e nunca fará com que seu Efeito seja ressincronizado. -#### Move dynamic objects and functions inside your Effect {/*move-dynamic-objects-and-functions-inside-your-effect*/} +#### Mova objetos e funções dinâmicos dentro de seu Efeito {/*move-dynamic-objects-and-functions-inside-your-effect*/} -If your object depends on some reactive value that may change as a result of a re-render, like a `roomId` prop, you can't pull it *outside* your component. You can, however, move its creation *inside* of your Effect's code: +Se o seu objeto depender de algum valor reativo que possa mudar como resultado de uma nova renderização, como um prop `roomId`, você não poderá movê-lo *para fora* do seu componente. No entanto, você pode transferir sua criação para dentro do código do seu Efeito: ```js {7-10,11,14} const serverUrl = 'https://localhost:1234'; @@ -953,24 +953,24 @@ function ChatRoom({ roomId }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... ``` -Now that `options` is declared inside of your Effect, it is no longer a dependency of your Effect. Instead, the only reactive value used by your Effect is `roomId`. Since `roomId` is not an object or function, you can be sure that it won't be *unintentionally* different. In JavaScript, numbers and strings are compared by their content: +Agora que `options` foi declarado dentro de seu Efeito, ele não é mais uma dependência de seu Efeito. Em vez disso, o único valor reativo usado por seu Efeito é `roomId`. Como `roomId` não é um objeto ou uma função, você pode ter certeza de que ele não será *intencionalmente* diferente. Em JavaScript, os números e as cadeias de caracteres são comparados por seu conteúdo: ```js {7-8} -// During the first render -const roomId1 = 'music'; +// Durante a primeira renderização +const roomId1 = 'música'; -// During the next render -const roomId2 = 'music'; +// Durante a próxima renderização +const roomId2 = 'música'; -// These two strings are the same! -console.log(Object.is(roomId1, roomId2)); // true +// Essas duas cordas são iguais! +console.log(Object.is(roomId1, roomId2)); // Verdadeiro ``` -Thanks to this fix, the chat no longer re-connects if you edit the input: +Graças a essa correção, o bate-papo não será mais reconectado se você editar a entrada: @@ -995,25 +995,25 @@ function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

Bem-vindo(a) à sala {roomId}!

setMessage(e.target.value)} /> ); } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); return ( <>
@@ -1024,14 +1024,14 @@ export default function App() { ``` ```js src/chat.js -export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server +export function createConnection(serverUrl, roomId) { + // Uma implementação real se conectaria ao servidor. return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando-se à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -1044,9 +1044,9 @@ button { margin-left: 10px; }
-However, it *does* re-connect when you change the `roomId` dropdown, as you would expect. +No entanto, ele se *reconecta* quando você altera o menu suspenso `roomId`, conforme esperado. -This works for functions, too: +Isso também funciona para funções: ```js {7-12,14} const serverUrl = 'https://localhost:1234'; @@ -1066,15 +1066,15 @@ function ChatRoom({ roomId }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Todas as dependências declaradas // ... ``` -You can write your own functions to group pieces of logic inside your Effect. As long as you also declare them *inside* your Effect, they're not reactive values, and so they don't need to be dependencies of your Effect. +Você pode escrever suas próprias funções para agrupar partes da lógica dentro do seu Efeito. Desde que você também as declare *dentro* do seu Efeito, elas não são valores reativos e, portanto, não precisam ser dependências do seu Efeito. -#### Read primitive values from objects {/*read-primitive-values-from-objects*/} +#### Leia valores primitivos a partir de objetos {/*read-primitive-values-from-objects*/} -Sometimes, you may receive an object from props: +Às vezes, você pode receber um objeto de props: ```js {1,5,8} function ChatRoom({ options }) { @@ -1084,11 +1084,11 @@ function ChatRoom({ options }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // ✅ All dependencies declared + }, [options]); // ✅ Todas as dependências declaradas // ... ``` -The risk here is that the parent component will create the object during rendering: +O risco aqui é que o componente pai crie o objeto durante a renderização: ```js {3-6} ``` -This would cause your Effect to re-connect every time the parent component re-renders. To fix this, read information from the object *outside* the Effect, and avoid having object and function dependencies: +Isso faria com que seu Efeito fosse reconectado toda vez que o componente pai fosse renderizado novamente. Para corrigir isso, leia as informações do objeto *fora* do Efeito e evite ter dependências de objetos e funções: ```js {4,7-8,12} function ChatRoom({ options }) { @@ -1114,15 +1114,15 @@ function ChatRoom({ options }) { }); connection.connect(); return () => connection.disconnect(); - }, [roomId, serverUrl]); // ✅ All dependencies declared + }, [roomId, serverUrl]); // ✅ Todas as dependências declaradas // ... ``` -The logic gets a little repetitive (you read some values from an object outside an Effect, and then create an object with the same values inside the Effect). But it makes it very explicit what information your Effect *actually* depends on. If an object is re-created unintentionally by the parent component, the chat would not re-connect. However, if `options.roomId` or `options.serverUrl` really are different, the chat would re-connect. +A lógica pode se tornar um pouco repetitiva (você lê alguns valores de um objeto fora de um Efeito e, em seguida, cria um objeto com os mesmos valores dentro do Efeito). Mas isso torna muito explícito em quais informações seu Efeito *realmente* depende. Se um objeto for recriado inadvertidamente pelo componente pai, o chat não será reconectado. No entanto, se `options.roomId` ou `options.serverUrl` forem realmente diferentes, o chat será reconectado. -#### Calculate primitive values from functions {/*calculate-primitive-values-from-functions*/} +#### Calcular valores primitivos a partir de funções {/*calculate-primitive-values-from-functions*/} -The same approach can work for functions. For example, suppose the parent component passes a function: +A mesma abordagem pode funcionar para funções. Por exemplo, suponha que o componente pai passe uma função: ```js {3-8} ``` -To avoid making it a dependency (and causing it to re-connect on re-renders), call it outside the Effect. This gives you the `roomId` and `serverUrl` values that aren't objects, and that you can read from inside your Effect: +Para evitar que ele se torne uma dependência (e cause a reconexão em novas renderizações), obtenha-o fora do Efeito. Isso fornece os valores `roomId` e `serverUrl` que não são objetos e que você pode ler de dentro do seu Efeito: ```js {1,4} function ChatRoom({ getOptions }) { @@ -1150,24 +1150,24 @@ function ChatRoom({ getOptions }) { }); connection.connect(); return () => connection.disconnect(); - }, [roomId, serverUrl]); // ✅ All dependencies declared + }, [roomId, serverUrl]); // ✅ Todas as dependências declaradas // ... ``` -This only works for [pure](/learn/keeping-components-pure) functions because they are safe to call during rendering. If your function is an event handler, but you don't want its changes to re-synchronize your Effect, [wrap it into an Effect Event instead.](#do-you-want-to-read-a-value-without-reacting-to-its-changes) +Isso só funciona para funções [puras](/learn/keeping-components-pure) porque elas são seguras para chamar durante a renderização. Se sua função for um manipulador de eventos, mas você não quiser que suas alterações ressincronizem seu Efeito, [envolva-a em um Evento de Efeito em vez disso.](#do-you-want-to-read-a-value-without-reacting-to-its-changes) -- Dependencies should always match the code. -- When you're not happy with your dependencies, what you need to edit is the code. -- Suppressing the linter leads to very confusing bugs, and you should always avoid it. -- To remove a dependency, you need to "prove" to the linter that it's not necessary. -- If some code should run in response to a specific interaction, move that code to an event handler. -- If different parts of your Effect should re-run for different reasons, split it into several Effects. -- If you want to update some state based on the previous state, pass an updater function. -- If you want to read the latest value without "reacting" it, extract an Effect Event from your Effect. -- In JavaScript, objects and functions are considered different if they were created at different times. -- Try to avoid object and function dependencies. Move them outside the component or inside the Effect. +- As dependências devem sempre refletir o código atual. +- Se você não estiver satisfeito com suas dependências, a edição deve ser feita no código. +- Ignorar o linter pode causar bugs confusos; você deve sempre evitar essa prática. +- Para remover uma dependência, você precisa “provar” ao linter que ela não é necessária. +- Se algum código deve ser executado em resposta a uma interação específica, mova esse código para um manipulador de eventos. +- Se diferentes partes do seu efeito devem ser executadas novamente por motivos distintos, divida-o em vários efeitos. +- Se você precisar atualizar um estado com base no estado anterior, utilize uma função atualizadora. +- Se você quiser ler o valor mais recente sem reagir a ele, extraia um Evento de Efeito do seu Efeito. +- Em JavaScript, objetos e funções são considerados diferentes se forem criados em momentos distintos. +- Evite depender de objetos e funções. Mova-os para fora do componente ou para dentro do Efeito. @@ -2110,7 +2110,7 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reacti connection.on('message', (msg) => onReceiveMessage(msg)); connection.connect(); return () => connection.disconnect(); - }, [roomId, isEncrypted]); // ✅ All dependencies declared + }, [roomId, isEncrypted]); // ✅ Todas as dependências declaradas ``` As a result, the chat re-connects only when something meaningful (`roomId` or `isEncrypted`) changes: From 3258843b04bcfc70e9d97677f1a6f1d04f5bb054 Mon Sep 17 00:00:00 2001 From: Anthoni Broering dos Santos Date: Mon, 26 Aug 2024 11:40:44 -0300 Subject: [PATCH 2/2] translation: Completed second part of the "removing-effect-dependencies" page --- .../learn/removing-effect-dependencies.md | 308 +++++++++--------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/src/content/learn/removing-effect-dependencies.md b/src/content/learn/removing-effect-dependencies.md index 2ccbebb44..d00cd6057 100644 --- a/src/content/learn/removing-effect-dependencies.md +++ b/src/content/learn/removing-effect-dependencies.md @@ -12,7 +12,7 @@ Quando você escreve um efeito, o linter verifica se você incluiu todos os valo - Como corrigir loops de dependência de efeito infinito - O que fazer quando você quiser remover uma dependência -- Como ler um valor de seu Effect sem “reagir” a ele +- Como ler um valor de seu Efeito sem “reagir” a ele - Como e por que evitar dependências de objetos e funções - Por que suprimir o linter de dependência é perigoso e o que fazer em vez disso @@ -273,7 +273,7 @@ Talvez você tenha notado um padrão em seu fluxo de trabalho: 2. Em seguida, você segue as orientações do linter e ajusta as dependências para **corresponder ao código que você alterou**. 3. Se não estiver satisfeito com a lista de dependências, você **volta ao primeiro passo** (e altera o código novamente). -A última parte é importante. **Se você deseja alterar as dependências, modifique o código ao redor primeiro.** Você pode considerar a lista de dependências como [uma lista de todos os valores reativos usados pelo código do seu Effect.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Você não *escolhe* o que colocar nessa lista. A lista *descreve* o seu código. Para alterar a lista de dependências, altere o código. +A última parte é importante. **Se você deseja alterar as dependências, modifique o código ao redor primeiro.** Você pode considerar a lista de dependências como [uma lista de todos os valores reativos usados pelo código do seu Efeito.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Você não *escolhe* o que colocar nessa lista. A lista *descreve* o seu código. Para alterar a lista de dependências, altere o código. Isso pode parecer como resolver uma equação. Você pode começar com um objetivo (por exemplo, remover uma dependência) e precisa “encontrar” o código que corresponda a esse objetivo. Nem todo mundo acha divertido resolver equações, e a mesma coisa pode ser dita sobre escrever efeitos! Felizmente, há uma lista de receitas comuns que você pode experimentar abaixo. @@ -555,7 +555,7 @@ O código final é mais longo que o original, mas a divisão desses Efeitos aind ### Você está lendo algum estado para calcular o próximo estado? {/*are-you-reading-some-state-to-calculate-the-next-state*/} -Esse Efeito atualiza a variável de estado `messages` com uma matriz recém-criada sempre que uma nova mensagem chega: +Este Efeito atualiza a variável de estado `messages` com uma *array* recém-criada sempre que chega uma nova mensagem: ```js {2,6-8} function ChatRoom({ roomId }) { @@ -569,7 +569,7 @@ function ChatRoom({ roomId }) { // ... ``` -Ele usa a variável `messages` para [criar uma nova matriz](/learn/updating-arrays-in-state) começando com todas as mensagens existentes e adicionando a nova mensagem no final. Entretanto, como `messages` é um valor reativo lido por um Effect, ele deve ser uma dependência: +Ele usa a variável `messages` para [criar um novo array](/learn/updating-arrays-in-state) começando com todas as mensagens existentes e adicionando a nova mensagem no final. Entretanto, como `messages` é um valor reativo lido por um Efeito, ele deve ser uma dependência: ```js {7,10} function ChatRoom({ roomId }) { @@ -587,7 +587,7 @@ function ChatRoom({ roomId }) { E tornar `messages` uma dependência introduz um problema. -Toda vez que você recebe uma mensagem, `setMessages()` faz com que o componente seja renderizado novamente com uma nova matriz `messages` que inclui a mensagem recebida. Entretanto, como esse Efeito agora depende de `messages`, isso *também* ressincronizará o Efeito. Portanto, cada nova mensagem fará com que o chat se reconecte. O usuário não gostaria disso! +Toda vez que você recebe uma mensagem, `setMessages()` faz com que o componente seja renderizado novamente com um novo *array* `messages` que inclui a mensagem recebida. Entretanto, como esse Efeito agora depende de `messages`, isso *também* ressincronizará o Efeito. Portanto, cada nova mensagem fará com que o chat se reconecte. O usuário não gostaria disso! Para corrigir o problema, não leia `messages` dentro do Efeito. Em vez disso, passe uma [função de atualização](/reference/react/useState#updating-state-based-on-the-previous-state) para `setMessages`: @@ -716,7 +716,7 @@ Suponha que o componente pai passe uma função `onReceiveMessage` *diferente* a /> ``` -Como o `onReceiveMessage` é uma dependência, isso faria com que o Effect fosse ressincronizado após cada nova renderização do pai. Isso faria com que ele se reconectasse ao chat. Para resolver isso, envolva a chamada em um Evento de Efeito: +Como o `onReceiveMessage` é uma dependência, isso faria com que o Efeito fosse ressincronizado após cada nova renderização do pai. Isso faria com que ele se reconectasse ao chat. Para resolver isso, envolva a chamada em um Evento de Efeito: ```js {4-6,12,15} function ChatRoom({ roomId, onReceiveMessage }) { @@ -933,7 +933,7 @@ function ChatRoom() { // ... ``` -Como o `createOptions` é declarado fora de seu componente, ele não é um valor reativo. É por isso que ele não precisa ser especificado nas dependências de seu Effect e nunca fará com que seu Efeito seja ressincronizado. +Como o `createOptions` é declarado fora de seu componente, ele não é um valor reativo. É por isso que ele não precisa ser especificado nas dependências de seu Efeito e nunca fará com que seu Efeito seja ressincronizado. #### Mova objetos e funções dinâmicos dentro de seu Efeito {/*move-dynamic-objects-and-functions-inside-your-effect*/} @@ -1173,13 +1173,13 @@ Isso só funciona para funções [puras](/learn/keeping-components-pure) porque -#### Fix a resetting interval {/*fix-a-resetting-interval*/} +#### Fixar um intervalo de reinicialização {/*fix-a-resetting-interval*/} -This Effect sets up an interval that ticks every second. You've noticed something strange happening: it seems like the interval gets destroyed and re-created every time it ticks. Fix the code so that the interval doesn't get constantly re-created. +Esse Efeito configura um intervalo que passa a cada segundo. Você notou que algo estranho está acontecendo: parece que o intervalo é destruído e recriado toda vez que ele faz tique-taque. Corrija o código para que o intervalo não seja recriado constantemente. -It seems like this Effect's code depends on `count`. Is there some way to not need this dependency? There should be a way to update the `count` state based on its previous value without adding a dependency on that value. +Parece que o código desse Efeito depende do `count`. Existe alguma maneira de não precisar dessa dependência? Deve haver uma maneira de atualizar o estado do `count` com base em seu valor anterior sem adicionar uma dependência a esse valor. @@ -1192,18 +1192,18 @@ export default function Timer() { const [count, setCount] = useState(0); useEffect(() => { - console.log('✅ Creating an interval'); + console.log('✅ Criando um intervalo'); const id = setInterval(() => { - console.log('⏰ Interval tick'); + console.log('⏰ Intervalo de tique-taque'); setCount(count + 1); }, 1000); return () => { - console.log('❌ Clearing an interval'); + console.log('❌ Limpar o intervalo'); clearInterval(id); }; }, [count]); - return

Counter: {count}

+ return

Contador: {count}

} ``` @@ -1211,9 +1211,9 @@ export default function Timer() { -You want to update the `count` state to be `count + 1` from inside the Effect. However, this makes your Effect depend on `count`, which changes with every tick, and that's why your interval gets re-created on every tick. +Você deseja atualizar o estado `count` para que seja `count + 1` de dentro do Efeito. Entretanto, isso faz com que seu Efeito dependa de `count`, que muda a cada tique-taque, e é por isso que seu intervalo é recriado a cada tique-taque. -To solve this, use the [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) and write `setCount(c => c + 1)` instead of `setCount(count + 1)`: +Para resolver isso, use a [função atualizadora](/reference/react/useState#updating-state-based-on-the-previous-state) e escreva `setCount(c => c + 1)` em vez de `setCount(count + 1)`: @@ -1224,36 +1224,36 @@ export default function Timer() { const [count, setCount] = useState(0); useEffect(() => { - console.log('✅ Creating an interval'); + console.log('✅ Criando um intervalo'); const id = setInterval(() => { - console.log('⏰ Interval tick'); + console.log('⏰ Intervalo de tique-taque'); setCount(c => c + 1); }, 1000); return () => { - console.log('❌ Clearing an interval'); + console.log('❌ Limpar o intervalo'); clearInterval(id); }; }, []); - return

Counter: {count}

+ return

Contador: {count}

} ```
-Instead of reading `count` inside the Effect, you pass a `c => c + 1` instruction ("increment this number!") to React. React will apply it on the next render. And since you don't need to read the value of `count` inside your Effect anymore, so you can keep your Effect's dependencies empty (`[]`). This prevents your Effect from re-creating the interval on every tick. +Em vez de ler o valor de `count` dentro do Efeito, você passa uma instrução `c => c + 1` ("incremente esse número!") para o React. O React aplicará essa instrução na próxima renderização. Como você não precisa mais ler o valor de `count` dentro do seu Efeito, você pode manter as dependências do Efeito vazias (`[]`). Isso evita que o Efeito recrie o intervalo a cada tique-taque.
-#### Fix a retriggering animation {/*fix-a-retriggering-animation*/} +#### Corrigir uma animação que está sendo reiniciada repetidamente {/*fix-a-retriggering-animation*/} -In this example, when you press "Show", a welcome message fades in. The animation takes a second. When you press "Remove", the welcome message immediately disappears. The logic for the fade-in animation is implemented in the `animation.js` file as plain JavaScript [animation loop.](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) You don't need to change that logic. You can treat it as a third-party library. Your Effect creates an instance of `FadeInAnimation` for the DOM node, and then calls `start(duration)` or `stop()` to control the animation. The `duration` is controlled by a slider. Adjust the slider and see how the animation changes. +Neste exemplo, ao pressionar "Mostrar", uma mensagem de boas-vindas aparece gradualmente. A animação leva um segundo. Quando você pressiona "Remover", a mensagem de boas-vindas desaparece imediatamente. A lógica para a animação de fade-in é implementada no arquivo `animation.js` como um [loop de animação](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) em JavaScript. Você não precisa alterar essa lógica, pois pode tratá-la como uma biblioteca de terceiros. Seu Efeito cria uma instância de `FadeInAnimation` para o nó do DOM e depois chama `start(duration)` ou `stop()` para controlar a animação. A `duration` é controlada por um *slider*. Ajuste o *slider* e veja como a animação muda. -This code already works, but there is something you want to change. Currently, when you move the slider that controls the `duration` state variable, it retriggers the animation. Change the behavior so that the Effect does not "react" to the `duration` variable. When you press "Show", the Effect should use the current `duration` on the slider. However, moving the slider itself should not by itself retrigger the animation. +Esse código já funciona, mas há algo que você deseja modificar. Atualmente, quando você move o *slider* que controla a variável de estado `duration`, ele reinicia a animação. Altere o comportamento para que o Efeito não "reaja" à variável `duration`. Quando você pressionar "Mostrar", o Efeito deve usar a `duration` atual do *slider*. No entanto, mover o *slider* em si não deve reiniciar a animação. -Is there a line of code inside the Effect that should not be reactive? How can you move non-reactive code out of the Effect? +Existe uma linha de código dentro do Efeito que não deveria ser reativa? Como você pode mover código não reativo para fora do Efeito? @@ -1303,7 +1303,7 @@ function Welcome({ duration }) { backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)' }} > - Welcome + Bem-vindo(a) ); } @@ -1323,10 +1323,10 @@ export default function App() { onChange={e => setDuration(Number(e.target.value))} />
- Fade in duration: {duration} ms + Duração do fade-in: {duration} ms
{show && } @@ -1343,11 +1343,11 @@ export class FadeInAnimation { start(duration) { this.duration = duration; if (this.duration === 0) { - // Jump to end immediately + // Ir para o final imediatamente this.onProgress(1); } else { this.onProgress(0); - // Start animating + // Comece a animar this.startTime = performance.now(); this.frameId = requestAnimationFrame(() => this.onFrame()); } @@ -1357,7 +1357,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 + // Ainda temos mais quadros para renderizar this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -1382,7 +1382,7 @@ html, body { min-height: 300px; } -Your Effect needs to read the latest value of `duration`, but you don't want it to "react" to changes in `duration`. You use `duration` to start the animation, but starting animation isn't reactive. Extract the non-reactive line of code into an Effect Event, and call that function from your Effect. +Seu Efeito precisa ler o valor mais recente de `duration`, mas você não quer que ele "reaja" às mudanças em `duration`. Você usa `duration` para iniciar a animação, mas iniciar a animação não é reativo. Extraia a linha de código não reativa para um Evento de Efeito e chame essa função a partir do seu Efeito. @@ -1434,7 +1434,7 @@ function Welcome({ duration }) { backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)' }} > - Welcome + Bem-vindo(a) ); } @@ -1454,10 +1454,10 @@ export default function App() { onChange={e => setDuration(Number(e.target.value))} />
- Fade in duration: {duration} ms + Duração do fade-in: {duration} ms
{show && } @@ -1482,7 +1482,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 + // Ainda temos mais quadros para renderizar this.frameId = requestAnimationFrame(() => this.onFrame()); } } @@ -1505,19 +1505,19 @@ html, body { min-height: 300px; }
-Effect Events like `onAppear` are not reactive, so you can read `duration` inside without retriggering the animation. +Eventos de Efeito, como `onAppear`, não são reativos, portanto você pode ler `duration` dentro sem disparar a animação.
-#### Fix a reconnecting chat {/*fix-a-reconnecting-chat*/} +#### Corrigir um chat que está se reconectando {/*fix-a-reconnecting-chat*/} -In this example, every time you press "Toggle theme", the chat re-connects. Why does this happen? Fix the mistake so that the chat re-connects only when you edit the Server URL or choose a different chat room. +Neste exemplo, toda vez que você pressiona "Alternar tema", o chat se reconecta. Por que isso acontece? Corrija o erro para que o chat se reconecte apenas quando você editar a URL do servidor ou escolher uma sala de chat diferente. -Treat `chat.js` as an external third-party library: you can consult it to check its API, but don't edit it. +Trate `chat.js` como uma biblioteca externa de terceiros: você pode consultá-la para verificar sua API, mas não edite o código. -There's more than one way to fix this, but ultimately you want to avoid having an object as your dependency. +Há mais de uma maneira de corrigir isso, mas, no final, você deve evitar ter um objeto como sua dependência. @@ -1529,7 +1529,7 @@ import ChatRoom from './ChatRoom.js'; export default function App() { const [isDark, setIsDark] = useState(false); - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); const [serverUrl, setServerUrl] = useState('https://localhost:1234'); const options = { @@ -1540,24 +1540,24 @@ export default function App() { return (

@@ -1578,25 +1578,25 @@ export default function ChatRoom({ options }) { return () => connection.disconnect(); }, [options]); - return

Welcome to the {options.roomId} room!

; + return

Bem-vindo(a) à sala {options.roomId}!

; } ``` ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -1611,9 +1611,9 @@ label, button { display: block; margin-bottom: 5px; } -Your Effect is re-running because it depends on the `options` object. Objects can be re-created unintentionally, you should try to avoid them as dependencies of your Effects whenever possible. +Seu Efeito está sendo reexecutado porque depende do objeto `options`. Objetos podem ser recriados involuntariamente, você deve tentar evitar usá-los como dependências dos seus Efeitos sempre que possível. -The least invasive fix is to read `roomId` and `serverUrl` right outside the Effect, and then make the Effect depend on those primitive values (which can't change unintentionally). Inside the Effect, create an object and pass it to `createConnection`: +A correção menos invasiva é ler `roomId` e `serverUrl` logo fora do Efeito e, em seguida, fazer com que o Efeito dependa desses valores primitivos (que não podem mudar involuntariamente). Dentro do Efeito, crie um objeto e passe-o para `createConnection`: @@ -1623,7 +1623,7 @@ import ChatRoom from './ChatRoom.js'; export default function App() { const [isDark, setIsDark] = useState(false); - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); const [serverUrl, setServerUrl] = useState('https://localhost:1234'); const options = { @@ -1634,24 +1634,24 @@ export default function App() { return (

@@ -1676,25 +1676,25 @@ export default function ChatRoom({ options }) { return () => connection.disconnect(); }, [roomId, serverUrl]); - return

Welcome to the {options.roomId} room!

; + return

Bem-vindo(a) à sala {options.roomId}!

; } ``` ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -1707,7 +1707,7 @@ label, button { display: block; margin-bottom: 5px; } -It would be even better to replace the object `options` prop with the more specific `roomId` and `serverUrl` props: +Seria ainda melhor substituir a propriedade de objeto `options` pelas propriedades mais específicas `roomId` e `serverUrl`: @@ -1717,30 +1717,30 @@ import ChatRoom from './ChatRoom.js'; export default function App() { const [isDark, setIsDark] = useState(false); - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); const [serverUrl, setServerUrl] = useState('https://localhost:1234'); return (

@@ -1767,25 +1767,25 @@ export default function ChatRoom({ roomId, serverUrl }) { return () => connection.disconnect(); }, [roomId, serverUrl]); - return

Welcome to the {roomId} room!

; + return

Bem-vindo(a) à sala {options.roomId}!

; } ``` ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Conectando à sala "' + roomId + '" em ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Desconectado da sala "' + roomId + '" em ' + serverUrl); } }; } @@ -1798,25 +1798,25 @@ label, button { display: block; margin-bottom: 5px; } -Sticking to primitive props where possible makes it easier to optimize your components later. +Manter-se aos *props* primitivos, quando possível, facilita a otimização futura dos seus componentes. -#### Fix a reconnecting chat, again {/*fix-a-reconnecting-chat-again*/} +#### Corrigir um chat que reconecta, novamente {/*fix-a-reconnecting-chat-again*/} -This example connects to the chat either with or without encryption. Toggle the checkbox and notice the different messages in the console when the encryption is on and off. Try changing the room. Then, try toggling the theme. When you're connected to a chat room, you will receive new messages every few seconds. Verify that their color matches the theme you've picked. +Neste exemplo, a conexão com o chat pode ser feita com ou sem criptografia. Marque e desmarque a caixa de seleção para notar as mensagens diferentes no console quando a criptografia está ativada ou desativada. Experimente mudar a sala. Em seguida, tente alternar o tema. Quando você está conectado a uma sala de chat, você receberá novas mensagens a cada poucos segundos. Verifique se a cor das mensagens corresponde ao tema que você escolheu. -In this example, the chat re-connects every time you try to change the theme. Fix this. After the fix, changing the theme should not re-connect the chat, but toggling encryption settings or changing the room should re-connect. +Neste exemplo, o chat reconecta toda vez que você tenta mudar o tema. Corrija isso. Após a correção, mudar o tema não deve reconectar o chat, mas alternar as configurações de criptografia ou mudar a sala deve reconectar. -Don't change any code in `chat.js`. Other than that, you can change any code as long as it results in the same behavior. For example, you may find it helpful to change which props are being passed down. +Não altere nenhum código em `chat.js`. Além disso, você pode alterar qualquer código, desde que resulte no mesmo comportamento. Por exemplo, pode ser útil alterar quais props estão sendo passadas. -You're passing down two functions: `onMessage` and `createConnection`. Both of them are created from scratch every time `App` re-renders. They are considered to be new values every time, which is why they re-trigger your Effect. +Você está passando duas funções: `onMessage` e `createConnection`. Ambas são criadas do zero toda vez que o `App` é renderizado novamente. Por isso, são consideradas como valores novos a cada renderização, o que faz com que o seu Efeito seja reexecutado. -One of these functions is an event handler. Do you know some way to call an event handler an Effect without "reacting" to the new values of the event handler function? That would come in handy! +Um desses manipuladores é um manipulador de eventos. Você conhece alguma forma de chamar um manipulador de eventos dentro de um Efeito sem que ele "reaja" aos novos valores da função do manipulador de eventos? Isso seria bastante útil! -Another of these functions only exists to pass some state to an imported API method. Is this function really necessary? What is the essential information that's being passed down? You might need to move some imports from `App.js` to `ChatRoom.js`. +Outra dessas funções existe apenas para passar um estado para um método de API importado. Essa função é realmente necessária? Qual é a informação essencial que está sendo passada adiante? Você pode precisar mover algumas importações de `App.js` para `ChatRoom.js`. @@ -1850,7 +1850,7 @@ import { showNotification } from './notifications.js'; export default function App() { const [isDark, setIsDark] = useState(false); - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); const [isEncrypted, setIsEncrypted] = useState(false); return ( @@ -1861,7 +1861,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Use dark theme + Usar tema escuro
{ - showNotification('New message: ' + msg, isDark ? 'dark' : 'light'); + showNotification('Nova mensagem: ' + msg, isDark ? 'dark' : 'light'); }} createConnection={() => { const options = { @@ -1917,29 +1917,29 @@ export default function ChatRoom({ roomId, createConnection, onMessage }) { return () => connection.disconnect(); }, [createConnection, onMessage]); - return

Welcome to the {roomId} room!

; + return

Bem-vindo(a) à sala {roomId}!

; } ``` ```js src/chat.js export function createEncryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ 🔐 Connecting to "' + roomId + '" room... (encrypted)'); + console.log('✅ 🔐 Conectando à sala "' + roomId + '"... (criptografado'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { if (Math.random() > 0.5) { - messageCallback('hey') + messageCallback('ei') } else { messageCallback('lol'); } @@ -1949,14 +1949,14 @@ export function createEncryptedConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ 🔐 Disconnected from "' + roomId + '" room (encrypted)'); + console.log('❌ 🔐 Desconectado da sala "' + roomId + '" (criptografada)'); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Não é possível adicionar o manipulador duas vezes.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Apenas o evento "message" é suportado.'); } messageCallback = callback; }, @@ -1964,23 +1964,23 @@ export function createEncryptedConnection({ serverUrl, roomId }) { } export function createUnencryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ Connecting to "' + roomId + '" room (unencrypted)...'); + console.log('✅ Conectando à sala "' + roomId + '" (sem criptografia)...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { if (Math.random() > 0.5) { - messageCallback('hey') + messageCallback('ei') } else { messageCallback('lol'); } @@ -1990,14 +1990,14 @@ export function createUnencryptedConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Disconnected from "' + roomId + '" room (unencrypted)'); + console.log('❌ Desconectado da sala "' + roomId + '" (sem criptografia)'); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Não é possível adicionar o manipulador duas vezes.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Apenas o evento "message" é suportado.'); } messageCallback = callback; }, @@ -2031,11 +2031,11 @@ label, button { display: block; margin-bottom: 5px; } -There's more than one correct way to solve this, but here is one possible solution. +Há mais de uma maneira correta de resolver isso, mas aqui está uma solução possível. -In the original example, toggling the theme caused different `onMessage` and `createConnection` functions to be created and passed down. Since the Effect depended on these functions, the chat would re-connect every time you toggle the theme. +No exemplo original, alternar o tema fazia com que diferentes funções `onMessage` e `createConnection` fossem criadas e passadas para baixo. Como o Efeito dependia dessas funções, o chat seria reconectado toda vez que o tema fosse alterado. -To fix the problem with `onMessage`, you needed to wrap it into an Effect Event: +Para resolver o problema com `onMessage`, você precisava envolvê-lo em um evento Efeito: ```js {1,2,6} export default function ChatRoom({ roomId, createConnection, onMessage }) { @@ -2047,21 +2047,21 @@ export default function ChatRoom({ roomId, createConnection, onMessage }) { // ... ``` -Unlike the `onMessage` prop, the `onReceiveMessage` Effect Event is not reactive. This is why it doesn't need to be a dependency of your Effect. As a result, changes to `onMessage` won't cause the chat to re-connect. +Diferente da prop `onMessage`, o Evento de Efeito `onReceiveMessage` não é reativo. É por isso que ele não precisa ser uma dependência do seu Efeito. Como resultado, mudanças no `onMessage` não farão com que o chat se reconecte. -You can't do the same with `createConnection` because it *should* be reactive. You *want* the Effect to re-trigger if the user switches between an encrypted and an unencryption connection, or if the user switches the current room. However, because `createConnection` is a function, you can't check whether the information it reads has *actually* changed or not. To solve this, instead of passing `createConnection` down from the `App` component, pass the raw `roomId` and `isEncrypted` values: +Você não pode fazer o mesmo com `createConnection` porque ele *deve* ser reativo. Você *quer* que o Efeito seja acionado novamente se o usuário trocar entre uma conexão criptografada e uma não criptografada, ou se o usuário trocar a sala atual. Entretanto, como `createConnection` é uma função, não é possível verificar se a informação lida mudou ou não. Para resolver isso, ao invés de passar `createConnection` do componente `App`, passe os valores brutos de `roomId` e `isEncrypted`: ```js {2-3} { - showNotification('New message: ' + msg, isDark ? 'dark' : 'light'); + showNotification('Nova mensagem: ' + msg, isDark ? 'dark' : 'light'); }} /> ``` -Now you can move the `createConnection` function *inside* the Effect instead of passing it down from the `App`: +Agora você pode mover a função `createConnection` para *dentro* do Efeito em vez de passá-la como uma propriedade de `App`: ```js {1-4,6,10-20} import { @@ -2087,19 +2087,19 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // ... ``` -After these two changes, your Effect no longer depends on any function values: +Após estas duas alterações, o seu Efeito já não depende de quaisquer valores de função: ```js {1,8,10,21} -export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reactive values - const onReceiveMessage = useEffectEvent(onMessage); // Not reactive +export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Valores reativos + const onReceiveMessage = useEffectEvent(onMessage); // Não reativo useEffect(() => { function createConnection() { const options = { serverUrl: 'https://localhost:1234', - roomId: roomId // Reading a reactive value + roomId: roomId // Leitura de um valor reativo }; - if (isEncrypted) { // Reading a reactive value + if (isEncrypted) { // Leitura de um valor reativo return createEncryptedConnection(options); } else { return createUnencryptedConnection(options); @@ -2113,7 +2113,7 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { // Reacti }, [roomId, isEncrypted]); // ✅ Todas as dependências declaradas ``` -As a result, the chat re-connects only when something meaningful (`roomId` or `isEncrypted`) changes: +Como resultado, o chat só volta a ligar-se quando algo significativo (`roomId` ou `isEncrypted`) muda: @@ -2142,7 +2142,7 @@ import { showNotification } from './notifications.js'; export default function App() { const [isDark, setIsDark] = useState(false); - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('geral'); const [isEncrypted, setIsEncrypted] = useState(false); return ( @@ -2153,7 +2153,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Use dark theme + Usar tema escuro
@@ -2179,7 +2179,7 @@ export default function App() { roomId={roomId} isEncrypted={isEncrypted} onMessage={msg => { - showNotification('New message: ' + msg, isDark ? 'dark' : 'light'); + showNotification('Nova mensagem: ' + msg, isDark ? 'dark' : 'light'); }} /> @@ -2217,29 +2217,29 @@ export default function ChatRoom({ roomId, isEncrypted, onMessage }) { return () => connection.disconnect(); }, [roomId, isEncrypted]); - return

Welcome to the {roomId} room!

; + return

Bem-vindo(a) à sala {roomId}!

; } ``` ```js src/chat.js export function createEncryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma cadeia de caracteres. Recebido: ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ 🔐 Connecting to "' + roomId + '" room... (encrypted)'); + console.log('✅ 🔐 Conectando à sala "' + roomId + '"... (criptografado)'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { if (Math.random() > 0.5) { - messageCallback('hey') + messageCallback('ei') } else { messageCallback('lol'); } @@ -2249,14 +2249,14 @@ export function createEncryptedConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ 🔐 Disconnected from "' + roomId + '" room (encrypted)'); + console.log('❌ 🔐 Desconectado da sala "' + roomId + '" (criptografada)'); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Não é possível adicionar o manipulador duas vezes.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Apenas o evento "message" é suportado.'); } messageCallback = callback; }, @@ -2264,23 +2264,23 @@ export function createEncryptedConnection({ serverUrl, roomId }) { } export function createUnencryptedConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Uma implementação real realmente se conectaria ao servidor if (typeof serverUrl !== 'string') { - throw Error('Expected serverUrl to be a string. Received: ' + serverUrl); + throw Error('Esperava-se que serverUrl fosse uma string. Recebido: ' + serverUrl); } if (typeof roomId !== 'string') { - throw Error('Expected roomId to be a string. Received: ' + roomId); + throw Error('Esperava-se que roomId fosse uma string. Recebido: ' + roomId); } let intervalId; let messageCallback; return { connect() { - console.log('✅ Connecting to "' + roomId + '" room (unencrypted)...'); + console.log('✅ Conectando à sala "' + roomId + '" (sem criptografado)...'); clearInterval(intervalId); intervalId = setInterval(() => { if (messageCallback) { if (Math.random() > 0.5) { - messageCallback('hey') + messageCallback('ei') } else { messageCallback('lol'); } @@ -2290,14 +2290,14 @@ export function createUnencryptedConnection({ serverUrl, roomId }) { disconnect() { clearInterval(intervalId); messageCallback = null; - console.log('❌ Disconnected from "' + roomId + '" room (unencrypted)'); + console.log('❌ Desconectado da sala "' + roomId + '" (sem criptografado)'); }, on(event, callback) { if (messageCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Não é possível adicionar o manipulador duas vezes.'); } if (event !== 'message') { - throw Error('Only "message" event is supported.'); + throw Error('Apenas o evento "message" é suportado.'); } messageCallback = callback; },