Atualizando Objetos no Estado
O estado pode conter qualquer tipo de valor JavaScript, incluindo objetos. Mas você não deve alterar objetos que estão no estado do React diretamente. Em vez disso, quando quiser atualizar um objeto, você precisa criar um novo (ou fazer uma cópia de um existente) e, em seguida, definir o estado para usar essa cópia.
Você aprenderá
- Como atualizar corretamente um objeto no estado do React
- Como atualizar um objeto aninhado sem mutá-lo
- O que é imutabilidade e como não quebrá-la
- Como tornar a cópia de objetos menos repetitiva com Immer
O que é uma mutação?
Você pode armazenar qualquer tipo de valor JavaScript no estado.
Até agora, você tem trabalhado com números, strings e booleanos. Esses tipos de valores JavaScript são "imutáveis", ou seja, inalteráveis ou "somente leitura". Você pode acionar uma nova renderização parasubstituirum valor:
O estadoxmudou de0para5, mas onúmero0em sinão mudou. Não é possível fazer alterações nos valores primitivos integrados como números, strings e booleanos em JavaScript.
Agora considere um objeto no estado:
Tecnicamente, é possível alterar o conteúdo dopróprio objeto.Isso é chamado de mutação:
No entanto, embora objetos no estado do React sejam tecnicamente mutáveis, você deve tratá-loscomo sefossem imutáveis — como números, booleanos e strings. Em vez de mutá-los, você deve sempre substituí-los.
Trate o estado como somente leitura
Em outras palavras, você devetratar qualquer objeto JavaScript que você colocar no estado como somente leitura.
Este exemplo mantém um objeto no estado para representar a posição atual do ponteiro. O ponto vermelho deve se mover quando você toca ou move o cursor sobre a área de visualização. Mas o ponto permanece na posição inicial:
O problema está neste trecho de código.
Este código modifica o objeto atribuído apositiondarenderização anterior.Mas sem usar a função de definição de estado, o React não tem como saber que o objeto mudou. Portanto, o React não faz nada em resposta. É como tentar mudar o pedido depois que você já comeu a refeição. Embora a mutação do estado possa funcionar em alguns casos, não a recomendamos. Você deve tratar o valor do estado ao qual tem acesso em uma renderização como somente leitura.
Para realmenteacionar uma nova renderizaçãoneste caso,crie umnovoobjeto e passe-o para a função de definição de estado:
ComsetPosition, você está dizendo ao React:
- Substitua
positionpor este novo objeto - E renderize este componente novamente
Observe como o ponto vermelho agora segue seu ponteiro quando você toca ou passa o mouse sobre a área de visualização:
Copiando objetos com a sintaxe de espalhamento
No exemplo anterior, o objetopositioné sempre criado novo a partir da posição atual do cursor. Mas frequentemente, você vai querer incluir dadosexistentescomo parte do novo objeto que você está criando. Por exemplo, você pode querer atualizarapenas umcampo em um formulário, mas manter os valores anteriores para todos os outros campos.
Estes campos de entrada não funcionam porque os manipuladoresonChangemutam o estado:
Por exemplo, esta linha muta o estado de uma renderização anterior:
A maneira confiável de obter o comportamento que você procura é criar um novo objeto e passá-lo parasetPerson. Mas aqui, você também quercopiar os dados existentes para eleporque apenas um dos campos mudou:
Você pode usar a sintaxe de espalhamento...de objetopara que você não precise copiar cada propriedade separadamente.
Agora o formulário funciona!
Observe que você não declarou uma variável de estado separada para cada campo de entrada. Para formulários grandes, manter todos os dados agrupados em um objeto é muito conveniente—desde que você o atualize corretamente!
Observe que a sintaxe de espalhamento...é "superficial"—ela copia as coisas apenas um nível de profundidade. Isso a torna rápida, mas também significa que se você quiser atualizar uma propriedade aninhada, terá que usá-la mais de uma vez.
Atualizando um objeto aninhado
Considere uma estrutura de objeto aninhada como esta:
Se você quisesse atualizarperson.artwork.city, fica claro como fazer isso com mutação:
Mas no React, você trata o estado como imutável! Para alterarcity, você primeiro precisaria produzir o novo objetoartwork(pré-preenchido com dados do anterior) e, em seguida, produzir o novo objetopersonque aponta para o novoartwork:
Ou, escrito como uma única chamada de função:
Isso fica um pouco prolixo, mas funciona bem para muitos casos:
Escreva lógica de atualização concisa com Immer
Se o seu estado estiver profundamente aninhado, você pode considerarachatá-lo.Mas, se você não quiser alterar a estrutura do seu estado, pode preferir um atalho para os spreads aninhados.Immeré uma biblioteca popular que permite escrever usando a sintaxe conveniente, mas mutante, e cuida de produzir as cópias para você. Com o Immer, o código que você escreve parece estar "quebrando as regras" e mutando um objeto:
Mas, diferentemente de uma mutação regular, ela não sobrescreve o estado anterior!
Para experimentar o Immer:
- Execute
npm install use-immerpara adicionar o Immer como uma dependência - Em seguida, substitua
import { useState } from 'react'porimport { useImmer } from 'use-immer'
Aqui está o exemplo acima convertido para usar o Immer:
Observe como os manipuladores de eventos ficaram muito mais concisos. Você pode misturar e combinaruseState e useImmerem um único componente o quanto quiser. O Immer é uma ótima maneira de manter os manipuladores de atualização concisos, especialmente se houver aninhamento em seu estado e a cópia de objetos levar a código repetitivo.
Recapitulação
- Trate todo estado no React como imutável.
- Quando você armazena objetos no estado, mutá-los não acionará renderizações e alterará o estado em "instantâneos" de renderizações anteriores.
- Em vez de mutar um objeto, crie umanovaversão dele e acione uma nova renderização definindo o estado para ela.
- Você pode usar a sintaxe de espalhamento de objeto
{...obj, something: 'newValue'}para criar cópias de objetos. - A sintaxe de espalhamento é superficial: ela copia apenas um nível de profundidade.
- Para atualizar um objeto aninhado, você precisa criar cópias em toda a cadeia, a partir do local que está atualizando.
- Para reduzir código de cópia repetitivo, use Immer.
Try out some challenges
Challenge 1 of 3:Fix incorrect state updates #
This form has a few bugs. Click the button that increases the score a few times. Notice that it does not increase. Then edit the first name, and notice that the score has suddenly “caught up” with your changes. Finally, edit the last name, and notice that the score has disappeared completely.
Your task is to fix all of these bugs. As you fix them, explain why each of them happens.
