Eu sei que o React pode executar atualizações de estado de forma assíncrona e em lote para otimização do desempenho. Portanto, você nunca pode confiar no estado a ser atualizado depois de ligar setState
. Mas você pode confiar Reagir para atualizar o estado na mesma ordem como setState
é chamado para
- o mesmo componente?
- componentes diferentes?
Considere clicar no botão nos seguintes exemplos:
1. Existe alguma possibilidade de que a seja falso eb seja verdadeiro para:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.setState({ a: true });
this.setState({ b: true });
}
}
2. Existe a possibilidade de que a seja falso eb seja verdadeiro para:
class SuperContainer extends React.Component {
constructor(props) {
super(props);
this.state = { a: false };
}
render() {
return <Container setParentState={this.setState.bind(this)}/>
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.props.setParentState({ a: true });
this.setState({ b: true });
}
}
Lembre-se de que essas são simplificações extremas do meu caso de uso. Percebo que posso fazer isso de maneira diferente, por exemplo, atualizando os dois parâmetros de estado ao mesmo tempo no exemplo 1, bem como executando a segunda atualização de estado em um retorno de chamada para a primeira atualização de estado no exemplo 2. No entanto, essa não é minha pergunta, e só estou interessado se houver uma maneira bem definida de o React executar essas atualizações de estado, nada mais.
Qualquer resposta apoiada pela documentação é muito apreciada.
fonte
Respostas:
Eu trabalho no React.
TLDR:
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 seChild
eParent
cada chamadasetState()
ao manipular um evento de clique, você não deseja renderizar novamenteChild
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, comoa
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 asetState(fn)
versão funcional que fornece o estado anterior, em vez de ler a partir dethis.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 umsetState()
seria processado imediatamente à medida que isso acontecesse. Neste caso, sim, você iria ver um estado intermediário: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 :
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çãounstable_batchedUpdates
duas vezes não tem efeito. As atualizações são liberadas quando saímos daunstable_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.
fonte
obj.a = true; obj.b = true
) e, no final, simplesmente fazerthis.setState(obj)
. Isso é seguro, independentemente de você estar dentro de um manipulador de eventos ou não. Pode ser um truque interessante, se você costuma cometer o erro de definir o estado várias vezes fora dos manipuladores de eventos.componentDidUpdate
e outros retornos de chamada do ciclo de vida a partir do React 16? Desde já, obrigado!Esta é realmente uma pergunta bastante interessante, mas a resposta não deve ser muito complicada. Há este ótimo artigo sobre suporte que tem uma resposta.
1) Se você fizer isso
Eu não acho que haverá uma situação onde
a
serátrue
eb
seráfalse
por causa do lote .No entanto, se
b
for dependentea
, pode realmente haver uma situação em que você não obteria o estado esperado.Depois que todas as chamadas acima forem processadas
this.state.value
, será 1, e não 3, como você esperaria.Isso é mencionado no artigo:
setState accepts a function as its parameter
Isso nos dará
this.state.value === 3
fonte
this.state.value
for atualizado nos manipuladores de eventos (ondesetState
está em lote) e nos retornos de chamada AJAX (onde nãosetState
está em lote). Nos manipuladores de eventos, eu usaria o para sempre ter certeza de que atualizo o estado usando o estado atualmente atualizado fornecido pela função. Devo usar com uma função atualizadora dentro do código do retorno de chamada AJAX, mesmo que eu saiba que não está em lote? Você poderia esclarecer o uso de um retorno de chamada AJAX com ou sem o uso de uma função atualizadora? Obrigado!updater function
setState
setState
setState
seriam executados, mas o último venceria.Várias chamadas durante o mesmo ciclo podem ser agrupadas em lote. Por exemplo, se você tentar incrementar uma quantidade de item mais de uma vez no mesmo ciclo, isso resultará no equivalente a:
https://reactjs.org/docs/react-component.html
fonte
como no doc
realizará a alteração como na fila ( FIFO : primeiro a entrar, primeiro a sair) a primeira chamada será a primeira a realizar
fonte