Enfileirando uma Série de Atualizações no State
Definir uma variável de estado irá 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, é interessante entender como o React agrupa atualizações de state.
Você aprenderá
- O que é “agrupamento” e como o React o utiliza para processar múltiplas atualizações de state
- Como aplicar várias atualizações à mesma variável de estado em sequência
O React agrupa atualizações de state
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 state são fixos em cada renderização, 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 rerrenderização acontece apenas depois de todas essas chamadas de setNumber()
.
Isso pode lembrar 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 rerrenderizaçõ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 “semi-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 garante que só faz o agrupamento quando normalmente for seguro fazê-lo. Isso garante que, por exemplo, se o primeiro clique de um botão desabilitar um formulário, o segundo clique não o submeta novamente.
Atualizando o mesmo state várias vezes antes da próxima renderização
É um caso de uso incomum, mas se você quiser atualizar o mesmo state várias vezes antes da próxima renderização, em vez de passar o próximo valor do state como setNumber(number + 1)
, você pode passar uma função que calcula o próximo state com base no anterior, como setNumber(n => n + 1)
. É uma maneira de dizer ao React para “fazer algo com o state” 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 state:
- 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 state 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 incrementa corretamente o valor em 3.
O que acontece se você atualizar o state 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 states:
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 state 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 states:
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 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 rerrenderização. Durante a rerrenderizaçã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 state 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 do state 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 state não muda a variável na renderização existente, mas solicita uma nova renderização.
- O React processa atualizações de state após os manipuladores de eventos terem terminado de executar. Isso é chamado de agrupamento.
- Para atualizar algum state 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 vários 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); }); }