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:


Á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:


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.


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.


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:


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.


QuandoCountermuda parap, oCounteré deletado e opé adicionado


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.


Quandosectionmuda paradiv, osectioné deletado e o novodivé adicionado


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:
- Renderizar componentes em posições diferentes
- Dar a cada componente uma identidade explícita com
key
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 um
Counter.


Estado inicial


Clicando em “próximo”


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:
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.
