v19.2Latest

Preservando e Resetando o Estado

O estado é isolado entre componentes. O React rastreia a qual componente cada estado pertence com base em sua posição na árvore de UI. Você pode controlar quando preservar o estado e quando resetá-lo entre re-renderizações.

Você aprenderá
  • Quando o React escolhe preservar ou resetar o estado
  • Como forçar o React a resetar o estado de um componente
  • Como chaves e tipos afetam se o estado é preservado

O estado está vinculado a uma posição na árvore de renderização

O React constróiárvores de renderizaçãopara a estrutura de componentes na sua UI.

Quando você dá estado a um componente, pode pensar que o estado "vive" dentro do componente. Mas o estado na verdade é mantido dentro do React. O React associa cada pedaço de estado que está mantendo ao componente correto com base em onde esse componente está localizado na árvore de renderização.

Aqui, há apenas uma tag JSX<Counter />, mas ela é renderizada em duas posições diferentes:

Veja como elas se parecem como uma árvore:

Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. Cada um dos filhos é rotulado 'Counter' e ambos contêm uma bolha de estado rotulada 'count' com valor 0.Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. Cada um dos filhos é rotulado 'Counter' e ambos contêm uma bolha de estado rotulada 'count' com valor 0.

Árvore do React

Estes são dois contadores separados porque cada um é renderizado em sua própria posição na árvore.Você geralmente não precisa pensar nessas posições para usar o React, mas pode ser útil entender como ele funciona.

No React, cada componente na tela tem um estado totalmente isolado. Por exemplo, se você renderizar dois componentesCounterlado a lado, cada um terá seus próprios estadosscore e hover, independentes.

Tente clicar em ambos os contadores e observe que eles não afetam um ao outro:

Como você pode ver, quando um contador é atualizado, apenas o estado desse componente é atualizado:

Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 1. A bolha de estado do filho direito está destacada em amarelo para indicar que seu valor foi atualizado.Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 1. A bolha de estado do filho direito está destacada em amarelo para indicar que seu valor foi atualizado.

Atualizando o estado

O React manterá o estado enquanto você renderizar o mesmo componente na mesma posição na árvore. Para ver isso, incremente ambos os contadores, depois remova o segundo componente desmarcando a caixa de seleção “Renderizar o segundo contador” e, em seguida, adicione-o novamente marcando-a:

Observe como, no momento em que você para de renderizar o segundo contador, seu estado desaparece completamente. Isso acontece porque, quando o React remove um componente, ele destrói seu estado.

Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito está faltando, e em seu lugar há uma imagem amarela de 'poof', destacando o componente sendo deletado da árvore.Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito está faltando, e em seu lugar há uma imagem amarela de 'poof', destacando o componente sendo excluído da árvore.

Excluindo um componente

Quando você marca “Renderizar o segundo contador”, um segundoCountere seu estado são inicializados do zero (score = 0) e adicionados ao DOM.

Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. Todo o nó filho direito está destacado em amarelo, indicando que ele acabou de ser adicionado à árvore.Diagrama de uma árvore de componentes React. O nó raiz é rotulado 'div' e tem dois filhos. O filho esquerdo é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho direito é rotulado 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. Todo o nó filho direito está destacado em amarelo, indicando que ele acabou de ser adicionado à árvore.

Adicionando um componente

O React preserva o estado de um componente enquanto ele está sendo renderizado em sua posição na árvore de UI.Se ele for removido, ou um componente diferente for renderizado na mesma posição, o React descarta seu estado.

O mesmo componente na mesma posição preserva o estado

Neste exemplo, existem duas tags<Counter />diferentes:

Quando você marca ou desmarca a caixa de seleção, o estado do contador não é redefinido. SejaisFancy trueoufalse, você sempre tem um<Counter />como o primeiro filho dodivretornado do componente raizApp:

Diagrama com duas seções separadas por uma seta de transição entre elas. Cada seção contém um layout de componentes com um pai rotulado 'App' contendo uma bolha de estado rotulada isFancy. Este componente tem um filho rotulado 'div', que leva a uma bolha de prop contendo isFancy (destacada em roxo) passada para o único filho. O último filho é rotulado 'Counter' e contém uma bolha de estado com rótulo 'count' e valor 3 em ambos os diagramas. Na seção esquerda do diagrama, nada está destacado e o valor do estado pai isFancy é false. Na seção direita do diagrama, o valor do estado pai isFancy mudou para true e está destacado em amarelo, assim como a bolha de props abaixo, que também mudou seu valor isFancy para true.Diagrama com duas seções separadas por uma seta de transição entre elas. Cada seção contém um layout de componentes com um pai rotulado 'App' contendo uma bolha de estado rotulada isFancy. Este componente tem um filho rotulado 'div', que leva a uma bolha de prop contendo isFancy (destacada em roxo) passada para o único filho. O último filho é rotulado 'Counter' e contém uma bolha de estado com rótulo 'count' e valor 3 em ambos os diagramas. Na seção esquerda do diagrama, nada está destacado e o valor do estado pai isFancy é false. Na seção direita do diagrama, o valor do estado pai isFancy mudou para true e está destacado em amarelo, assim como a bolha de props abaixo, que também mudou seu valor isFancy para true.

Atualizar o estado doAppnão redefine oCounterporque oCounterpermanece na mesma posição

É o mesmo componente na mesma posição, então, da perspectiva do React, é o mesmo contador.

Armadilha

Lembre-se de queé a posição na árvore de UI—não na marcação JSX—que importa para o React!Este componente tem duas cláusulasreturncom diferentes tags JSX<Counter />dentro e fora doif:

Você pode esperar que o estado seja redefinido quando você marca a caixa de seleção, mas isso não acontece! Isso ocorre porqueambas essas tags<Counter />são renderizadas na mesma posição.O React não sabe onde você coloca as condições em sua função. Tudo que ele “vê” é a árvore que você retorna.

Em ambos os casos, o componenteAppretorna um<div>com<Counter />como primeiro filho. Para o React, esses dois contadores têm o mesmo “endereço”: o primeiro filho do primeiro filho da raiz. É assim que o React os combina entre as renderizações anteriores e seguintes, independentemente de como você estrutura sua lógica.

Componentes diferentes na mesma posição redefinem o estado

Neste exemplo, marcar a caixa de seleção substituirá<Counter>por um<p>:

Aqui, você alterna entrediferentestipos de componente na mesma posição. Inicialmente, o primeiro filho da<div>continha umCounter. Mas quando você trocou por ump, o React removeu oCounterda árvore de UI e destruiu seu estado.

Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 3. A seção do meio tem o mesmo pai 'div', mas o componente filho foi agora deletado, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'p', destacado em amarelo.Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 3. A seção do meio tem o mesmo pai 'div', mas o componente filho foi agora deletado, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'p', destacado em amarelo.

QuandoCountermuda parap, oCounteré deletado e opé adicionado

Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'p'. A seção do meio tem o mesmo pai 'div', mas o componente filho foi agora deletado, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, destacado em amarelo.Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'p'. A seção do meio tem o mesmo pai 'div', mas o componente filho foi agora deletado, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, destacado em amarelo.

Ao alternar de volta, opé deletado e oCounteré adicionado

Além disso,quando você renderiza um componente diferente na mesma posição, ele redefine o estado de toda a sua subárvore.Para ver como isso funciona, incremente o contador e depois marque a caixa de seleção:

O estado do contador é redefinido quando você clica na caixa de seleção. Embora você renderize umCounter, o primeiro filho dadivmuda de umsectionpara umdiv. Quando o filhosectionfoi removido do DOM, toda a árvore abaixo dele (incluindo oCountere seu estado) também foi destruída.

Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'section', que tem um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 3. A seção do meio tem o mesmo pai 'div', mas os componentes filhos foram agora deletados, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'div', destacado em amarelo, também com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, todos destacados em amarelo.Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'section', que tem um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 3. A seção do meio tem o mesmo pai 'div', mas os componentes filhos foram agora deletados, indicado por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'div', destacado em amarelo, também com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, todos destacados em amarelo.

Quandosectionmuda paradiv, osectioné deletado e o novodivé adicionado

Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'div', que tem um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0. A seção do meio tem o mesmo pai 'div', mas os componentes filhos foram excluídos, indicados por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'section', destacado em amarelo, também com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, todos destacados em amarelo.Diagrama com três seções, com uma seta transitando entre cada seção. A primeira seção contém um componente React rotulado 'div' com um único filho rotulado 'div', que tem um único filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0. A seção do meio tem o mesmo pai 'div', mas os componentes filhos foram excluídos, indicados por uma imagem amarela de 'prova'. A terceira seção tem o mesmo pai 'div' novamente, agora com um novo filho rotulado 'section', destacado em amarelo, também com um novo filho rotulado 'Counter' contendo uma bolha de estado rotulada 'count' com valor 0, todos destacados em amarelo.

Ao alternar de volta, odivé excluído e o novosectioné adicionado

Como regra geral,se você quiser preservar o estado entre re-renderizações, a estrutura da sua árvore precisa "corresponder"de uma renderização para outra. Se a estrutura for diferente, o estado é destruído porque o React destrói o estado quando remove um componente da árvore.

Armadilha

É por isso que você não deve aninhar definições de funções de componente.

Aqui, a função do componenteMyTextFieldé definidadentroMyComponent:

Toda vez que você clica no botão, o estado da entrada desaparece! Isso ocorre porque uma funçãodiferenteMyTextFieldé criada para cada renderização deMyComponent. Você está renderizando um componentediferentena mesma posição, então o React redefine todo o estado abaixo. Isso leva a bugs e problemas de desempenho. Para evitar esse problema,sempre declare funções de componente no nível superior e não aninhe suas definições.

Redefinindo o estado na mesma posição

Por padrão, o React preserva o estado de um componente enquanto ele permanece na mesma posição. Geralmente, isso é exatamente o que você quer, então faz sentido como comportamento padrão. Mas às vezes, você pode querer redefinir o estado de um componente. Considere este aplicativo que permite que dois jogadores acompanhem suas pontuações durante cada turno:

Atualmente, quando você muda o jogador, a pontuação é preservada. Os doisCounters aparecem na mesma posição, então o React os vê comoo mesmoCountercuja proppersonmudou.

Mas conceitualmente, neste aplicativo eles deveriam ser dois contadores separados. Eles podem aparecer no mesmo lugar na interface do usuário, mas um é um contador para Taylor e outro é um contador para Sarah.

Existem duas maneiras de redefinir o estado ao alternar entre eles:

  1. Renderizar componentes em posições diferentes
  2. Dar a cada componente uma identidade explícita comkey

Opção 1: Renderizar um componente em posições diferentes

Se você quiser que esses doisCounters sejam independentes, você pode renderizá-los em duas posições diferentes:

  • Inicialmente,isPlayerA é true. Portanto, a primeira posição contém o estado doCounter, e a segunda está vazia.
  • Quando você clica no botão “Próximo jogador”, a primeira posição é limpa, mas a segunda agora contém umCounter.
Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'true'. O único filho, disposto à esquerda, está rotulado como Counter com uma bolha de estado rotulada 'count' e valor 0. Todo o filho da esquerda está destacado em amarelo, indicando que foi adicionado.Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'true'. O único filho, disposto à esquerda, está rotulado como Counter com uma bolha de estado rotulada 'count' e valor 0. Todo o filho da esquerda está destacado em amarelo, indicando que foi adicionado.

Estado inicial

Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'false'. A bolha de estado está destacada em amarelo, indicando que mudou. O filho da esquerda é substituído por uma imagem amarela de 'poof' indicando que foi excluído e há um novo filho à direita, destacado em amarelo indicando que foi adicionado. O novo filho está rotulado como 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0.Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'false'. A bolha de estado está destacada em amarelo, indicando que mudou. O filho da esquerda é substituído por uma imagem amarela de 'poof' indicando que foi excluído e há um novo filho à direita, destacado em amarelo indicando que foi adicionado. O novo filho está rotulado como 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0.

Clicando em “próximo”

Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'true'. A bolha de estado está destacada em amarelo, indicando que mudou. Há um novo filho à esquerda, destacado em amarelo indicando que foi adicionado. O novo filho está rotulado como 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho da direita é substituído por uma imagem amarela de 'poof' indicando que foi excluído.Diagrama com uma árvore de componentes React. O pai está rotulado como 'Scoreboard' com uma bolha de estado rotulada isPlayerA com valor 'true'. A bolha de estado está destacada em amarelo, indicando que mudou. Há um novo filho à esquerda, destacado em amarelo indicando que foi adicionado. O novo filho está rotulado como 'Counter' e contém uma bolha de estado rotulada 'count' com valor 0. O filho da direita é substituído por uma imagem amarela de 'poof' indicando que foi excluído.

Clicando em “próximo” novamente

O estado de cadaCounteré destruído toda vez que ele é removido do DOM. É por isso que eles são redefinidos cada vez que você clica no botão.

Esta solução é conveniente quando você tem apenas alguns componentes independentes renderizados no mesmo lugar. Neste exemplo, você só tem dois, então não é um problema renderizar ambos separadamente no JSX.

Opção 2: Redefinindo o estado com uma chave

Existe também outra maneira, mais genérica, de redefinir o estado de um componente.

Você pode ter vistokeys aorenderizar listas.As chaves não são apenas para listas! Você pode usar chaves para fazer o React distinguir entre quaisquer componentes. Por padrão, o React usa a ordem dentro do pai (“primeiro contador”, “segundo contador”) para discernir entre componentes. Mas as chaves permitem que você diga ao React que este não é apenas umprimeirocontador, ou umsegundocontador, mas um contador específico—por exemplo, o contador daTaylor. Dessa forma, o React saberá o contador daTayloronde quer que ele apareça na árvore!

Neste exemplo, os dois<Counter />s não compartilham estado, mesmo que apareçam no mesmo lugar no JSX:

Alternar entre Taylor e Sarah não preserva o estado. Isso ocorre porquevocê deu a eleskeys diferentes:

Especificar umakeydiz ao React para usar a própriakeycomo parte da posição, em vez de sua ordem dentro do pai. É por isso que, mesmo que você os renderize no mesmo lugar no JSX, o React os vê como dois contadores diferentes e, portanto, eles nunca compartilharão estado. Cada vez que um contador aparece na tela, seu estado é criado. Cada vez que é removido, seu estado é destruído. Alternar entre eles redefine seu estado repetidamente.

Observação

Lembre-se de que as chaves não são globalmente únicas. Elas apenas especificam a posiçãodentro do pai.

Redefinindo um formulário com uma chave

Redefinir o estado com uma chave é particularmente útil ao lidar com formulários.

Neste aplicativo de chat, o componente<Chat>contém o estado da entrada de texto:

Tente digitar algo no campo de entrada e, em seguida, pressione “Alice” ou “Bob” para escolher um destinatário diferente. Você notará que o estado da entrada é preservado porque o<Chat>é renderizado na mesma posição na árvore.

Em muitos aplicativos, esse pode ser o comportamento desejado, mas não em um aplicativo de chat!Você não quer permitir que o usuário envie uma mensagem que já digitou para a pessoa errada devido a um clique acidental. Para corrigir isso, adicione umakey:

Isso garante que, ao selecionar um destinatário diferente, o componenteChatserá recriado do zero, incluindo qualquer estado na árvore abaixo dele. O React também recriará os elementos DOM em vez de reutilizá-los.

Agora, alternar o destinatário sempre limpa o campo de texto:

Deep Dive
Preservando o estado para componentes removidos

Recapitulação

  • O React mantém o estado enquanto o mesmo componente é renderizado na mesma posição.
  • O estado não é mantido nas tags JSX. Ele está associado à posição na árvore em que você coloca essa JSX.
  • Você pode forçar uma subárvore a redefinir seu estado dando a ela uma chave diferente.
  • Não aninhe definições de componentes, ou você redefinirá o estado por acidente.

Try out some challenges

Challenge 1 of 5:Fix disappearing input text #

This example shows a message when you press the button. However, pressing the button also accidentally resets the input. Why does this happen? Fix it so that pressing the button does not reset the input text.