Eu trabalho no React.
TLDR:
Mas você pode confiar em React para atualizar o estado na mesma ordem em que setState é chamado para
Sim.
Sim.
A ordem das atualizações é sempre respeitada. Se você vê um estado intermediário "entre" eles ou não, depende se você está dentro de um lote ou não.
Atualmente (React 16 e versões anteriores), somente as atualizações dentro dos manipuladores de eventos React são agrupadas por padrão . Existe uma API instável para forçar lotes fora dos manipuladores de eventos para casos raros quando você precisar.
Nas versões futuras (provavelmente React 17 e posterior), o React irá agrupar todas as atualizações por padrão, para que você não precise pensar nisso. Como sempre, anunciaremos quaisquer alterações sobre isso no blog do React e nas notas de versão.
A chave para entender isso é que, não importa quantas setState()
chamadas sejam feitas em quantos componentes você faz dentro de um manipulador de eventos React , eles produzirão apenas uma única re-renderização no final do evento . Isso é crucial para um bom desempenho em aplicativos grandes, pois se Child
e Parent
cada chamada setState()
ao manipular um evento de clique, você não deseja renderizar novamente Child
duas vezes.
Nos dois exemplos, as setState()
chamadas acontecem dentro de um manipulador de eventos React. Portanto, eles sempre são liberados juntos no final do evento (e você não vê o estado intermediário).
As atualizações são sempre mescladas superficialmente na ordem em que ocorrem . Portanto, se a primeira atualização for {a: 10}
, a segunda for {b: 20}
e a terceira for {a: 30}
, o estado renderizado será {a: 30, b: 20}
. A atualização mais recente para a mesma chave de estado (por exemplo, como a
no meu exemplo) sempre "vence".
O this.state
objeto é atualizado quando renderizamos novamente a interface do usuário no final do lote. Portanto, se você precisar atualizar o estado com base em um estado anterior (como incrementar um contador), use a setState(fn)
versão funcional que fornece o estado anterior, em vez de ler a partir de this.state
. Se você está curioso sobre o motivo disso, expliquei-o em profundidade neste comentário .
No seu exemplo, não veríamos o "estado intermediário" porque estamos dentro de um manipulador de eventos React em que o lote está ativado (porque o React "sabe" quando estamos saindo desse evento).
No entanto, nas versões React 16 e versões anteriores, ainda não há lotes por padrão fora dos manipuladores de eventos React . Portanto, se no seu exemplo tivéssemos um manipulador de resposta AJAX em vez de handleClick
, cada um setState()
seria processado imediatamente à medida que isso acontecesse. Neste caso, sim, você iria ver um estado intermediário:
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
Percebemos que é inconveniente que o comportamento seja diferente, dependendo de você estar em um manipulador de eventos ou não . Isso será alterado em uma versão futura do React que agrupará todas as atualizações por padrão (e fornecerá uma API de aceitação para liberar as alterações de forma síncrona). Até mudarmos o comportamento padrão (potencialmente no React 17), há uma API que você pode usar para forçar o lote :
promise.then(() => {
// Forces batching
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
});
// When we exit unstable_batchedUpdates, re-renders once
});
Os manipuladores de eventos React internamente estão sendo unstable_batchedUpdates
agrupados e é por isso que eles são agrupados em lotes por padrão. Observe que agrupar uma atualização unstable_batchedUpdates
duas vezes não tem efeito. As atualizações são liberadas quando saímos da unstable_batchedUpdates
chamada mais externa .
Essa API é "instável" no sentido de removê-la quando o lote já estiver ativado por padrão. No entanto, não o removeremos em uma versão secundária, para que você possa confiar com segurança até o React 17, se precisar forçar o lote em alguns casos fora dos manipuladores de eventos do React.
Para resumir, este é um tópico confuso, porque o React apenas faz lotes dentro de manipuladores de eventos por padrão. Isso mudará em versões futuras, e o comportamento será mais direto. Mas a solução não é agrupar menos , é agrupar mais por padrão. É isso que vamos fazer.