Por que o setState está no reactjs Async em vez de Sync?

126

Acabei de descobrir que a this.setState()função react em qualquer componente é assíncrona ou é chamada após a conclusão da função na qual foi chamada.

Agora pesquisei e encontrei este blog (a operação de mutação do estado setState () pode ser síncrona no ReactJS )

Aqui ele descobriu que setStateé assíncrono (chamado quando a pilha está vazia) ou sincronizado (chamado assim que chamado), dependendo de como a mudança de estado foi acionada.

Agora, essas duas coisas são difíceis de digerir

  1. No blog, a setStatefunção é chamada dentro de uma função updateState, mas o que acionou a updateStatefunção não é algo que uma função chamada saiba.
  2. Por que eles fazem setStateassíncrono como JS é uma linguagem de encadeamento único e este setState não é uma chamada WebAPI ou de servidor, portanto, isso deve ser feito apenas no encadeamento de JS. Eles estão fazendo isso para que a nova renderização não pare todos os ouvintes de eventos e outras coisas, ou há algum outro problema de design.
Anup
fonte
1
Eu escrevi um artigo hoje que ajuda a descrever um pouco do clima ao redor setState: medium.com/@agm1984/...
agm1984

Respostas:

155

Você pode chamar uma função depois que o valor do estado for atualizado:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Além disso, se você tiver vários estados para atualizar de uma vez, agrupe-os todos da mesma maneira setState:

Ao invés de:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Apenas faça o seguinte:

this.setState({
    foo: "one",
    bar: "two"
});
JoeTidee
fonte
16
ya ok, temos uma função callBack que podemos usar, mas isso não é a pergunta.
Anup
12
Espero que ajude outra pessoa a se deparar com essa questão.
precisa saber é o seguinte
2
ya dat pode ser útil
Anup
97

1) as setStateações são assíncronas e são agrupadas em lotes para obter ganhos de desempenho. Isso é explicado na documentação de setState.

setState () não muda imediatamente this.state, mas cria uma transição de estado pendente. Acessar this.state depois de chamar esse método pode potencialmente retornar o valor existente. Não há garantia de operação síncrona de chamadas para setState e as chamadas podem ser agrupadas em lotes para obter ganhos de desempenho.


2) Por que eles tornariam o setState assíncrono como JS é uma linguagem encadeada única e essa setStatenão é uma chamada WebAPI ou de servidor?

Isso ocorre porque setStatealtera o estado e causa a nova renderização. Isso pode ser uma operação cara e torná-lo síncrono pode deixar o navegador sem resposta.

Assim, as chamadas setState são assíncronas e também agrupadas em lotes para melhor experiência e desempenho da interface do usuário.

Sachin
fonte
59
Se você precisar garantir a ordem dos eventos após a realização de uma chamada setState, poderá passar uma função de retorno de chamada. this.setState({ something: true }, () => console.log(this.state))
Ianks
1
Obrigado @Sachin pela explicação. No entanto, ainda tenho dúvidas, pode ser síncrono como o blog explica?
Ajay Gaur
2
Outra decisão estúpida de design em reagir. Torne a atualização do estado síncrona e a renderização assíncrona. Você pode renderizar em lote, mas eu quero poder definir algo tão primitivo quanto as variáveis ​​de estado sem precisar lidar com as condições de corrida.
ig-dev
Por que não permitir que uma opção seja definida para tornar a função assíncrona ou sincronizada? Isso seria um recurso útil
Randall Codificação
Obviamente, não estou na equipe de reação, mas um motivo, na minha opinião, para fazer a atualização de estado assíncrona é que os navegadores são únicos. As operações de sincronização podem deixar a interface do usuário sem resposta e não são boas candidatas à interface do usuário.
Sachin
16

Eu sei que essa pergunta é antiga, mas vem causando muita confusão para muitos usuários do reactjs há muito tempo, inclusive eu. Recentemente, Dan Abramov (da equipe de reação) acabou de escrever uma ótima explicação sobre o motivo de a natureza de setStateser assíncrona:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setStatedeve ser assíncrono, e existem algumas boas razões para isso na explicação vinculada de Dan Abramov. Isso não significa que ele irá sempre ser assíncrona - Principalmente significa que você simplesmente não pode depender de ele ser síncrono . O ReactJS leva em consideração muitas variáveis ​​no cenário em que você está alterando o estado, para decidir quando o atual statedeve realmente ser atualizado e seu componente novamente renderizado.
Um exemplo simples para demonstrar isso é que, se você chamar setStatecomo uma reação a uma ação do usuário, stateprovavelmente será atualizado imediatamente (embora, novamente, você não possa contar com isso), para que o usuário não sinta nenhum atraso , mas se você ligarsetState em reação a uma resposta de chamada ajax ou a algum outro evento que não seja acionado pelo usuário, o estado poderá ser atualizado com um pequeno atraso, pois o usuário não sentirá esse atraso e melhorará o desempenho aguardando agrupe várias atualizações de estado em conjunto e renderize o DOM menos vezes.

gillyb
fonte
você não marcou nenhum respondedor como o correto. As pessoas estão postando como contornar isso. Não é a resposta para a pergunta. Este artigo parece bom.
Anup
@ Anup A resposta é um pouco mais complicada do que apenas 'assíncrona' ou 'sincronizada'. Sempre deve ser tratado como 'assíncrono', mas em alguns casos pode atuar como 'sincronizado'. Espero lançar alguma luz para você.
gillyb
8

Bom artigo aqui https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

ou passar retorno de chamada this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

zloctb
fonte
7

Você pode usar o seguinte agrupamento para fazer chamadas de sincronização

this.setState((state =>{
  return{
    something
  }
})

Ярослав
fonte
resposta subestimada
James Cat
1

Imagine incrementar um contador em algum componente:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Há um manipulador de contagem anexado aos componentes pai e filho. Isso é feito propositadamente, para que possamos executar o setState () duas vezes no mesmo contexto de bolhas de eventos de clique, mas dentro de dois manipuladores diferentes.

Como poderíamos imaginar, um evento de clique único no botão agora acionaria esses dois manipuladores, pois o evento passa do alvo para o contêiner mais externo durante a fase de bolhas.

Portanto, o btnCountHandler () é executado primeiro, com o objetivo de incrementar a contagem para 1 e, em seguida, o divCountHandler () é executado, com o objetivo de incrementar a contagem para 2.

No entanto, a contagem apenas aumenta para 1, como você pode inspecionar nas ferramentas do React Developer.

Isso prova que reagem

  • enfileira todas as chamadas setState

  • volta para esta fila depois de executar o último método no contexto (o divCountHandler neste caso)

  • mescla todas as mutações de objetos que ocorrem em várias chamadas setState no mesmo contexto (todas as chamadas de métodos em uma única fase de evento são o mesmo contexto, por exemplo) em uma sintaxe de mutação de um único objeto (a fusão faz sentido porque é por isso que podemos atualizar as propriedades do estado independentemente em setState () em primeiro lugar)

  • e o passa para um único setState () para evitar a nova renderização devido a várias chamadas setState () (esta é uma descrição muito primitiva do lote).

Código resultante executado por react:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Para interromper esse comportamento, em vez de passar objetos como argumentos para o método setState, retornos de chamada são passados.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Depois que o último método conclui a execução e quando o react retorna para processar a fila setState, ele simplesmente chama o retorno de chamada para cada setState na fila, passando no estado anterior do componente.

Dessa maneira, o React garante que o último retorno de chamada na fila atualize o estado em que todos os seus colegas anteriores se comprometeram.

supi
fonte
0

Sim, setState () é assíncrono.

No link: https://reactjs.org/docs/react-component.html#setstate

  • O React não garante que as alterações de estado sejam aplicadas imediatamente.
  • setState () nem sempre atualiza imediatamente o componente.
  • Pense em setState () como uma solicitação e não como um comando imediato para atualizar o componente.

Porque eles pensam
No link: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... concordamos que a nova renderização do setState () seria ineficiente em muitos casos

O setState () assíncrono dificulta a vida dos iniciantes e até experimenta, infelizmente:
- problemas inesperados de renderização: renderização atrasada ou nenhuma renderização (com base na lógica do programa)
- passar parâmetros é um grande problema
entre outros problemas.

O exemplo abaixo ajudou:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Espero que ajude.

Manohar Reddy Poreddy
fonte