Enfileiramento de 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 múltiplas operações sobre o valor antes de enfileirar a próxima renderização. Para fazer isso, ajuda entender como o React agrupa atualizações de estado.
Você aprenderá
- O que é “agrupamento” e como o React o utiliza para processar múltiplas atualizações de estado
- Como aplicar várias atualizações à mesma variável de estado em sequência
O React agrupa atualizações de estado
Você pode esperar que clicar no botão “+3” incremente o contador três vezes porque 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ê deve 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. O React espera até que todo o código nos manipuladores de eventos tenha sido executado antes de processar suas atualizações de estado. É por isso que a re-renderização acontece apenas depois de todas essas chamadas setNumber()
.
Isso pode lembrar você de um garçom anotando um pedido no restaurante. Um garçom não corre para a cozinha ao mencionar seu primeiro prato! Em vez disso, ele deixa você terminar seu pedido, permite que você faça alterações e até pega pedidos de outras pessoas na mesa.

Illustrated by Rachel Lee Nabors
Isso permite que você atualize várias variáveis de estado—mesmo de múltiplos componentes—sem acionar muitas re-renderizações. Mas isso também significa que a interface do usuário não será atualizada até depois que seu manipulador de eventos e qualquer código nele tenham sido concluídos. Esse comportamento, também conhecido como agrupamento, faz seu aplicativo React funcionar muito mais rápido. Ele também evita lidar com renderizações “metade acabadas” confusas, onde apenas algumas das variáveis foram atualizadas.
O React não agrupa eventos intencionais múltiplos como cliques—cada clique é tratado separadamente. Fique tranquilo que o React só faz agrupamento quando é geralmente seguro fazê-lo. Isso garante que, por exemplo, se o primeiro clique do botão desabilitar um formulário, o segundo clique não o submeta novamente.
Atualizando o mesmo estado várias vezes antes da próxima renderização
É um caso de uso incomum, mas se você quiser 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 maneira de dizer ao React para “fazer algo com o valor de 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
é chamada de função atualizadora. Quando você a passa para um setter de estado:
- O React enfileira essa função para ser processada após todo o outro código no manipulador de eventos ter sido executado.
- Durante a próxima renderização, o React percorre a fila e te fornece o estado final atualizado.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
Aqui está como o React trabalha através dessas linhas de código enquanto executa o manipulador de eventos:
setNumber(n => n + 1)
:n => n + 1
é uma função. O React a adiciona a uma fila.setNumber(n => n + 1)
:n => n + 1
é uma função. O React a adiciona a uma fila.setNumber(n => n + 1)
:n => n + 1
é uma função. O React a adiciona a uma fila.
Quando você chama useState
durante a próxima renderização, o React percorre a fila. O estado anterior de number
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 |
O React armazena 3
como o resultado final e o retorna de useState
.
É por isso que clicar em “+3” no exemplo acima corretamente incrementa 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); }}>Aumentar 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)
. O React adiciona “substituir por5
” à sua fila.setNumber(n => n + 1)
:n => n + 1
é uma função atualizadora. O React adiciona essa função à sua fila.
Durante a próxima renderização, o React percorre a fila de estados:
atualização enfileirada | n | retorna |
---|---|---|
”substituir por 5 ” | 0 (não usado) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
O 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); }}>Aumentar o número</button> </> ) }
Aqui está como o React trabalha através dessas linhas de código enquanto executa este manipulador de eventos:
setNumber(number + 5)
:number
é0
, entãosetNumber(0 + 5)
. O React adiciona “substituir por5
” à sua fila.setNumber(n => n + 1)
:n => n + 1
é uma função atualizadora. O React adiciona essa função à sua fila.setNumber(42)
: O React adiciona “substituir por42
” à sua fila.
Durante a próxima renderização, o React percorre a fila de estados:
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 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.
Depois que o manipulador de eventos termina, o React acionará uma re-renderização. Durante a re-renderização, o React processará a fila. Funções atualizadoras executam durante a renderização, então funções atualizadoras devem ser puras e apenas retornar o resultado. Não tente definir o estado a partir de dentro delas ou executar outros efeitos colaterais. No Modo Estrito, o React irá 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 estado não muda a variável na renderização existente, mas solicita uma nova renderização.
- O React processa atualizações de estado após os manipuladores de eventos terem terminado de executar. Isso é chamado de agrupamento.
- Para atualizar algum estado várias vezes em um evento, você pode usar uma função atualizadora
setNumber(n => n + 1)
.
Challenge 1 of 2: Corrigir um contador de pedidos
Você está trabalhando em um aplicativo de mercado de arte que permite ao usuário enviar múltiplos pedidos para um item de arte ao mesmo tempo. Cada vez que o usuário pressiona o botão “Comprar”, o contador de “Pendentes” deve aumentar em um. Depois de três segundos, o contador de “Pendentes” deve diminuir, e o contador de “Concluídos” deve aumentar.
No entanto, o contador de “Pendentes” não se comporta como esperado. Quando você pressiona “Comprar”, ele diminui para -1
(o que não deveria ser possível!). E se você clicar rapidamente duas vezes, ambos os contadores parecem se comportar de maneira 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> Pendentes: {pending} </h3> <h3> Concluídos: {completed} </h3> <button onClick={handleClick}> Comprar </button> </> ); } function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); }