Separando Eventos de Efeitos
Os manipuladores de eventos só são reexecutados quando você realiza a mesma interação novamente. Diferente dos manipuladores de eventos, os Efeitos são ressincronizados se algum valor que eles leem, como uma prop ou uma variável de estado, for diferente do que era durante a última renderização. Às vezes, você também quer uma mistura dos dois comportamentos: um Efeito que é reexecutado em resposta a alguns valores, mas não a outros. Esta página vai te ensinar como fazer isso.
Você aprenderá
- Como escolher entre um manipulador de eventos e um Efeito
- Por que os Efeitos são reativos e os manipuladores de eventos não são
- O que fazer quando você quer que parte do código do seu Efeito não seja reativo
- O que são Eventos de Efeito e como extraí-los dos seus Efeitos
- Como ler as props e o estado mais recentes dos Efeitos usando Eventos de Efeito
Escolhendo entre manipuladores de eventos e Efeitos
Primeiro, vamos recapitular a diferença entre manipuladores de eventos e Efeitos.
Imagine que você está implementando um componente de sala de chat. Seus requisitos são assim:
- Seu componente deve se conectar automaticamente à sala de chat selecionada.
- Quando você clica no botão "Enviar", ele deve enviar uma mensagem para o chat.
Digamos que você já implementou o código para eles, mas não tem certeza de onde colocá-lo. Você deve usar manipuladores de eventos ou Efeitos? Sempre que precisar responder a essa pergunta, considerepor que o código precisa ser executado.
Manipuladores de eventos são executados em resposta a interações específicas
Da perspectiva do usuário, o envio de uma mensagem deve acontecerporqueo botão específico "Enviar" foi clicado. O usuário ficará bastante chateado se você enviar sua mensagem em qualquer outro momento ou por qualquer outro motivo. É por isso que enviar uma mensagem deve ser um manipulador de eventos. Manipuladores de eventos permitem que você lide com interações específicas:
Com um manipulador de eventos, você pode ter certeza de quesendMessage(message)será executadosomentese o usuário pressionar o botão.
Efeitos são executados sempre que a sincronização é necessária
Lembre-se de que você também precisa manter o componente conectado à sala de chat. Onde esse código vai?
O motivopara executar este código não é alguma interação específica. Não importa por que ou como o usuário navegou até a tela da sala de chat. Agora que eles estão olhando para ela e poderiam interagir com ela, o componente precisa permanecer conectado ao servidor de chat selecionado. Mesmo se o componente da sala de chat fosse a tela inicial do seu aplicativo, e o usuário não tivesse realizado nenhuma interação, vocêaindaprecisaria conectar. É por isso que é um Efeito:
Com este código, você pode ter certeza de que sempre há uma conexão ativa com o servidor de chat atualmente selecionado,independentementedas interações específicas realizadas pelo usuário. Seja o usuário apenas abrindo seu aplicativo, selecionando uma sala diferente, ou navegando para outra tela e voltando, seu Efeito garante que o componentepermanecerá sincronizadocom a sala atualmente selecionada, e iráreconectar sempre que necessário.
Valores reativos e lógica reativa
Intuitivamente, você poderia dizer que os manipuladores de eventos são sempre acionados "manualmente", por exemplo, ao clicar em um botão. Os Efeitos, por outro lado, são "automáticos": eles são executados e reexecutados quantas vezes for necessário para permanecerem sincronizados.
Há uma maneira mais precisa de pensar sobre isso.
Props, estado e variáveis declaradas dentro do corpo do seu componente são chamadas devalores reativos. Neste exemplo,serverUrlnão é um valor reativo, masroomId e messagesão. Eles participam do fluxo de dados de renderização:
Valores reativos como estes podem mudar devido a uma nova renderização. Por exemplo, o usuário pode editar amessageou escolher umroomIddiferente em um menu suspenso. Manipuladores de eventos e Efeitos respondem às mudanças de forma diferente:
- A lógica dentro dos manipuladores de eventosnão é reativa.Ela não será executada novamente a menos que o usuário realize a mesma interação (por exemplo, um clique) novamente. Manipuladores de eventos podem ler valores reativos sem "reagir" às suas mudanças.
- A lógica dentro dos Efeitosé reativa.Se o seu Efeito lê um valor reativo,você precisa especificá-lo como uma dependência.Então, se uma nova renderização causar a mudança desse valor, o React executará novamente a lógica do seu Efeito com o novo valor.
Vamos revisitar o exemplo anterior para ilustrar essa diferença.
A lógica dentro dos manipuladores de eventos não é reativa
Dê uma olhada nesta linha de código. Essa lógica deveria ser reativa ou não?
Da perspectiva do usuário,uma mudança namessage nãosignifica que ele quer enviar uma mensagem.Significa apenas que o usuário está digitando. Em outras palavras, a lógica que envia uma mensagem não deve ser reativa. Ela não deve ser executada novamente apenas porque ovalor reativomudou. É por isso que ela pertence ao manipulador de eventos:
Manipuladores de eventos não são reativos, entãosendMessage(message)só será executado quando o usuário clicar no botão Enviar.
A lógica dentro dos Efeitos é reativa
Agora vamos voltar a estas linhas:
Da perspectiva do usuário,uma mudança noroomIdsimsignifica que ele quer se conectar a uma sala diferente.Em outras palavras, a lógica para conectar-se à sala deve ser reativa. Vocêquerque estas linhas de código "acompanhem" ovalor reativoe sejam executadas novamente se esse valor for diferente. É por isso que ela pertence a um Efeito:
Os Efeitos são reativos, entãocreateConnection(serverUrl, roomId) e connection.connect()serão executados para cada valor distinto deroomId. Seu Efeito mantém a conexão do chat sincronizada com a sala atualmente selecionada.
Extraindo lógica não reativa dos Efeitos
As coisas ficam mais complicadas quando você quer misturar lógica reativa com lógica não reativa.
Por exemplo, imagine que você queira mostrar uma notificação quando o usuário se conectar ao chat. Você lê o tema atual (escuro ou claro) das props para poder mostrar a notificação na cor correta:
No entanto,themeé um valor reativo (ele pode mudar como resultado de uma nova renderização), etodo valor reativo lido por um Efeito deve ser declarado como sua dependência.Agora você precisa especificarthemecomo uma dependência do seu Efeito:
Experimente este exemplo e veja se consegue identificar o problema com esta experiência do usuário:
Quando oroomIdmuda, o chat reconecta-se como seria de esperar. Mas comothemetambém é uma dependência, o chattambémreconecta-se sempre que alternas entre o tema escuro e o claro. Isso não é bom!
Por outras palavras, tunão queresque esta linha seja reativa, mesmo que esteja dentro de um Efeito (que é reativo):
Precisas de uma forma de separar esta lógica não reativa do Efeito reativo que a envolve.
Declarar um Evento de Efeito
Usa um Hook especial chamadouseEffectEventpara extrair esta lógica não reativa do teu Efeito:
Aqui,onConnectedé chamado deEvento de Efeito.É uma parte da lógica do seu Efeito, mas se comporta muito mais como um manipulador de eventos. A lógica dentro dele não é reativa e sempre "vê" os valores mais recentes das suas props e estado.
Agora você pode chamar o Evento de EfeitoonConnectedde dentro do seu Efeito:
Isso resolve o problema. Observe que você teve queremoverthemeda lista de dependências do seu Efeito, porque ele não é mais usado no Efeito. Você também não precisaadicionaronConnecteda ela, porqueEventos de Efeito não são reativos e devem ser omitidos das dependências.
Verifique se o novo comportamento funciona como esperado:
Você pode pensar nos Eventos de Efeito como sendo muito semelhantes aos manipuladores de eventos. A principal diferença é que os manipuladores de eventos são executados em resposta a interações do usuário, enquanto os Eventos de Efeito são acionados por você a partir de Efeitos. Os Eventos de Efeito permitem que você "quebre a cadeia" entre a reatividade dos Efeitos e o código que não deve ser reativo.
Lendo as props e o estado mais recentes com Eventos de Efeito
Eventos de Efeito permitem que você corrija muitos padrões onde você pode ser tentado a suprimir o linter de dependências.
Por exemplo, digamos que você tenha um Efeito para registrar as visitas à página:
Mais tarde, você adiciona várias rotas ao seu site. Agora seu componentePagerecebe uma propurlcom o caminho atual. Você quer passar aurlcomo parte da sua chamadalogVisit, mas o linter de dependências reclama:
Pense no que você quer que o código faça. Vocêquerregistrar uma visita separada para URLs diferentes, pois cada URL representa uma página diferente. Em outras palavras, esta chamadalogVisit deveser reativa em relação àurl. É por isso que, neste caso, faz sentido seguir o linter de dependências e adicionarurlcomo uma dependência:
Agora digamos que você queira incluir o número de itens no carrinho de compras junto com cada visita à página:
Você usounumberOfItemsdentro do Effect, então o linter pede que você o adicione como uma dependência. No entanto, vocênãoquer que a chamadalogVisitseja reativa em relação anumberOfItems. Se o usuário colocar algo no carrinho de compras e onumberOfItemsmudar, issonão significaque o usuário visitou a página novamente. Em outras palavras,visitar a páginaé, de certo modo, um "evento". Acontece em um momento preciso no tempo.
Divida o código em duas partes:
Aqui,onVisité um Evento de Effect. O código dentro dele não é reativo. É por isso que você pode usarnumberOfItems(ou qualquer outro valor reativo!) sem se preocupar que isso fará o código ao redor ser reexecutado em mudanças.
Por outro lado, o Effect em si permanece reativo. O código dentro do Effect usa a propurl, então o Effect será reexecutado após cada nova renderização com umaurldiferente. Isso, por sua vez, chamará o Evento de EffectonVisit.
Como resultado, você chamarálogVisitpara cada mudança naurle sempre lerá onumberOfItemsmais recente. No entanto, senumberOfItemsmudar por si só, isso não fará com que nenhum código seja reexecutado.
Observação
Você pode estar se perguntando se poderia chamaronVisit()sem argumentos e ler aurldentro dela:
Isso funcionaria, mas é melhor passar estaurlpara o Evento de Effect explicitamente.Ao passarurl
Limitações dos Eventos de Efeito
Os Eventos de Efeito são muito limitados em como você pode usá-los:
- Chame-os apenas de dentro de Efeitos.
- Nunca os passe para outros componentes ou Hooks.
Por exemplo, não declare e passe um Evento de Efeito assim:
Em vez disso, declare sempre os Eventos de Efeito diretamente ao lado dos Efeitos que os utilizam:
Eventos de Efeito são "partes" não reativas do seu código de Efeito. Eles devem ficar ao lado do Efeito que os utiliza.
Recapitulação
- Manipuladores de eventos são executados em resposta a interações específicas.
- Efeitos são executados sempre que a sincronização é necessária.
- A lógica dentro dos manipuladores de eventos não é reativa.
- A lógica dentro dos Efeitos é reativa.
- Você pode mover a lógica não reativa dos Efeitos para Eventos de Efeito.
- Chame Eventos de Efeito apenas de dentro de Efeitos.
- Não passe Eventos de Efeito para outros componentes ou Hooks.
Try out some challenges
Challenge 1 of 4:Fix a variable that doesn’t update #
This Timer component keeps a count state variable which increases every second. The value by which it’s increasing is stored in the increment state variable. You can control the increment variable with the plus and minus buttons.
However, no matter how many times you click the plus button, the counter is still incremented by one every second. What’s wrong with this code? Why is increment always equal to 1 inside the Effect’s code? Find the mistake and fix it.
