Enfileirando uma Série de Atualizações de Estado
Definir uma variável de estado enfileirará outra renderização. Mas às vezes você pode querer realizar várias operações no valor antes de enfileirar a próxima renderização. Para fazer isso, é útil entender como o React agrupa atualizações de estado.
Você aprenderá
- O que é “agrupamento” e como o React o usa para processar várias atualizações de estado
- Como aplicar várias atualizações à mesma variável de estado em sequência
React agrupa atualizações de estado
Você pode esperar que clicar no botão “+3” incremente o contador três vezes porque ele chama setNumber(number + 1)
três vezes:
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 1); setNumber(number + 1); setNumber(number + 1); }}>+3</button> </> ) }
No entanto, como você pode se lembrar da seção anterior, os valores de estado de cada renderização são fixos, então o valor de number
dentro do manipulador de eventos da primeira renderização é sempre 0
, não importa quantas vezes você chame setNumber(1)
:
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
Mas há um outro fator em jogo aqui. React espera até que todo o código nos manipuladores de eventos seja executado antes de processar suas atualizações de estado. É por isso que a re-renderização só acontece depois de todas estas chamadas setNumber()
.
Isso pode lembrá-lo de um garçom fazendo um pedido no restaurante. Um garçom não corre para a cozinha ao mencionar seu primeiro prato! Em vez disso, eles deixam você terminar seu pedido, permitem que você faça alterações e até mesmo recebem pedidos de outras pessoas na mesa.

Illustrated by Rachel Lee Nabors
Isso permite que você atualize várias variáveis de estado - mesmo de vários componentes - sem acionar muitas re-renderizações. Mas isso também significa que a UI não será atualizada até depois que o seu manipulador de eventos, e qualquer código nele, for concluído. Este comportamento, também conhecido como agrupamento, faz com que seu aplicativo React execute muito mais rápido. Ele também evita lidar com renderizações “semi-acabadas” confusas, onde apenas algumas das variáveis foram atualizadas.
O React não agrupa em vários eventos intencionais como cliques - cada clique é tratado separadamente. Tenha certeza de que o React só faz agrupamento quando geralmente é seguro fazê-lo. Isso garante que, por exemplo, se o primeiro clique em um botão desabilitar um formulário, o segundo clique não o enviará novamente.
Atualizando o mesmo estado várias vezes antes da próxima renderização
É um caso de uso incomum, mas se você deseja atualizar a mesma variável de estado várias vezes antes da próxima renderização, em vez de passar o próximo valor de estado como setNumber(number + 1)
, você pode passar uma função que calcula o próximo estado com base no anterior na fila, como setNumber(n => n + 1)
. É uma forma de dizer ao React para “fazer algo com o valor do estado” em vez de apenas substituí-lo.
Tente incrementar o contador agora:
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(n => n + 1); setNumber(n => n + 1); setNumber(n => n + 1); }}>+3</button> </> ) }
Aqui, n => n + 1
é chamado de função atualizadora. Quando você a passa para um setter de estado:
- React enfileira esta função para ser processada após a execução de todo o outro código no manipulador de eventos.
- Durante a próxima renderização, o React passa pela fila e fornece o estado final atualizado.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
Veja como o React funciona com estas linhas de código enquanto executa o manipulador de eventos:
setNumber(n => n + 1)
:n => n + 1
é uma função. React adiciona-a a uma fila.setNumber(n => n + 1)
:n => n + 1
é uma função. React adiciona-a a uma fila.setNumber(n => n + 1)
:n => n + 1
é uma função. React adiciona-a a uma fila.
Quando você chama useState
durante a próxima renderização, o React passa pela fila. O estado number
anterior era 0
, então é isso que o React passa para a primeira função atualizadora como o argumento n
. Então o React pega o valor de retorno da sua função atualizadora anterior e o passa para a próxima atualizadora como n
, e assim por diante:
atualização enfileirada | n | retorna |
---|---|---|
n => n + 1 | 0 | 0 + 1 = 1 |
n => n + 1 | 1 | 1 + 1 = 2 |
n => n + 1 | 2 | 2 + 1 = 3 |
React armazena 3
como o resultado final e o retorna de useState
.
É por isso que clicar em “+3” no exemplo acima incrementa corretamente o valor em 3.
O que acontece se você atualizar o estado após substituí-lo
E quanto a este manipulador de eventos? O que você acha que number
será na próxima renderização?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 5); setNumber(n => n + 1); }}>Aumente o número</button> </> ) }
Aqui está o que este manipulador de eventos diz ao React para fazer:
setNumber(number + 5)
:number
é0
, entãosetNumber(0 + 5)
. React adiciona “substituir por5
” à sua fila.setNumber(n => n + 1)
:n => n + 1
é uma função atualizadora. React adiciona essa função à sua fila.
Durante a próxima renderização, o React percorre a fila de estado:
atualização enfileirada | n | retorna |
---|---|---|
”substituir por 5 ” | 0 (não usado) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
React armazena 6
como o resultado final e o retorna de useState
.
O que acontece se você substituir o estado após atualizá-lo
Vamos tentar mais um exemplo. O que você acha que number
será na próxima renderização?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 5); setNumber(n => n + 1); setNumber(42); }}>Aumente o número</button> </> ) }
Veja como o React trabalha com estas linhas de código enquanto executa este manipulador de eventos:
setNumber(number + 5)
:number
é0
, entãosetNumber(0 + 5)
. React adiciona “substituir por5
” à sua fila.setNumber(n => n + 1)
:n => n + 1
é uma função atualizadora. React adiciona essa função à sua fila.setNumber(42)
: React adiciona “substituir por42
” à sua fila.
Durante a próxima renderização, o React percorre a fila de estado:
atualização enfileirada | n | retorna |
---|---|---|
”substituir por 5 ” | 0 (não usado) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
”substituir por 42 ” | 6 (não usado) | 42 |
Então React armazena 42
como o resultado final e o retorna de useState
.
Para resumir, aqui está como você pode pensar sobre o que você está passando para o setter de estado setNumber
:
- Uma função atualizadora (por exemplo,
n => n + 1
) é adicionada à fila. - Qualquer outro valor (por exemplo, o número
5
) adiciona “substituir por5
” à fila, ignorando o que já está enfileirado.
Após a conclusão do manipulador de eventos, o React acionará uma nova renderização. Durante a re-renderização, o React processará a fila. As funções atualizadoras são executadas durante a renderização, portanto, as funções atualizadoras devem ser puras e apenas retornar o resultado. Não tente definir o estado de dentro delas ou executar outros efeitos colaterais. No Modo Strict, o React executará cada função atualizadora duas vezes (mas descartará o segundo resultado) para ajudar você a encontrar erros.
Convenções de nomenclatura
É comum nomear o argumento da função atualizadora pelas primeiras letras da variável de estado correspondente:
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
Se você preferir um código mais verboso, outra convenção comum é repetir o nome completo da variável de estado, como setEnabled(enabled => !enabled)
, ou usar um prefixo como setEnabled(prevEnabled => !prevEnabled)
.
Recap
- Definir o estado não altera a variável na renderização existente, mas solicita uma nova renderização.
- React processa atualizações de estado após a conclusão da execução dos manipuladores de eventos. Isso é chamado de agrupamento.
- Para atualizar algum estado várias vezes em um evento, você pode usar a função atualizadora
setNumber(n => n + 1)
.
Challenge 1 of 2: Corrigir um contador de solicitação
Você está trabalhando em um aplicativo de mercado de arte que permite ao usuário enviar vários pedidos de um item de arte ao mesmo tempo. Toda vez que o usuário pressiona o botão “Comprar”, o contador “Pendente” deve aumentar em um. Após três segundos, o contador “Pendente” deve diminuir e o contador “Concluído” deve aumentar.
No entanto, o contador “Pendente” não se comporta como o esperado. Quando você pressiona “Comprar”, ele diminui para -1
(o que não deveria ser possível!). E se você clicar duas vezes rapidamente, ambos os contadores parecem se comportar de forma imprevisível.
Por que isso acontece? Corrija ambos os contadores.
import { useState } from 'react'; export default function RequestTracker() { const [pending, setPending] = useState(0); const [completed, setCompleted] = useState(0); async function handleClick() { setPending(pending + 1); await delay(3000); setPending(pending - 1); setCompleted(completed + 1); } return ( <> <h3> Pendente: {pending} </h3> <h3> Concluído: {completed} </h3> <button onClick={handleClick}> Comprar </button> </> ); } function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); }