ReactJS: setTimeout () não está funcionando?

100

Tendo este código em mente:

var Component = React.createClass({

    getInitialState: function () {
        return {position: 0};    
    },

    componentDidMount: function () {
        setTimeout(this.setState({position: 1}), 3000);
    },

    render: function () {
         return (
            <div className="component">
                {this.state.position}
            </div>
         ); 
    }

});

ReactDOM.render(
    <Component />,
    document.getElementById('main')
);

O estado não deveria mudar somente após 3 segundos? Está mudando imediatamente.

Meu objetivo principal aqui é mudar o estado a cada 3 segundos (com setInterval()), mas como não estava funcionando, tentei setTimeout(), mas também não está funcionando. Alguma luz acesa? Obrigado!

Jbarradas
fonte
2
Se tiver, foo(bar())então baré executado primeiro e seu valor de retorno é passado para foo.
Felix Kling
@FelixKling que parece correto, mas não apropriado. Já que o foo()aqui é exatamente para executar barapós o tempo limite desejado. Ou estou completamente errado e ele executa na hora, e só retorna valor após o tempo desejado?
jbarradas
3
"Já que o foo () aqui é exatamente para executar bar após o tempo limite desejado." Certo, é por isso que você tem que passar bar, não chamar e passar seu valor de retorno. Você esperava que o comportamento de foo(bar())mudasse, dependendo do que fooestá acontecendo? Isso seria muito estranho.
Felix Kling

Respostas:

241

Faz

setTimeout(
    function() {
        this.setState({ position: 1 });
    }
    .bind(this),
    3000
);

Caso contrário, você está passando o resultado de setStatepara setTimeout.

Você também pode usar as funções de seta ES6 para evitar o uso de thispalavras-chave:

setTimeout(
  () => this.setState({ position: 1 }), 
  3000
);
Daniel A. White
fonte
1
Sim, faz sentido e está funcionando. Mas function () não é uma função? Então, por que precisaríamos vinculá-lo? Já tentei e preciso muito, só queria saber o porquê.
Agradeço
Não entendo porque você está dizendo que passaria o resultado para setTimeout, como isso não faz funcionar? Qual é o comportamento nesse caso?
PositiveGuy
16
para aqueles que preferem usar as funções de seta ES6: setTimeout(() => {this.setState({ position: 1 })}, 3000)@PositiveGuy não tenho certeza se você pesquisou isso por conta própria desde que esta questão foi postada, mas caso não o tenha feito: o exemplo original de Daniel precisa .bind(this)restringir o thiscontexto para setState- caso contrário , thisirá se referir automaticamente ao contexto no qual ele é chamado (neste caso, o anônimo functionsendo passado setTimeout). As funções de seta ES6, no entanto, têm escopo léxico - elas se restringem thisao contexto no qual são chamadas.
Zac Collier
1
Não funciona ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('Vamos acontecer?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Adicionar Nova Organização'});}}, 100); Está no contexto da classe sintática sugar class Organizations extends Component {console.log nunca obtém console.log ('Será que vamos acontecer?'); Tudo antes e depois de ser registrado.
juslintek de
@juslintek define não funciona. por favor, faça uma nova pergunta se necessário.
Daniel A. White
150
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

O acima também funcionaria porque a função de seta ES6 não altera o contexto de this.

Steven Scaffidi
fonte
3
A sintaxe ES6 deve ser a resposta aceita para as melhores práticas no React. Ambos vão funcionar, mas isso é mais elegante e alças this.
mccambridge
24

Sempre que criarmos um tempo limite, devemos limpá-lo no componentWillUnmount, se ainda não tiver disparado.

      let myVar;
         const Component = React.createClass({

            getInitialState: function () {
                return {position: 0};    
            },

            componentDidMount: function () {
                 myVar = setTimeout(()=> this.setState({position: 1}), 3000)
            },

            componentWillUnmount: () => {
              clearTimeout(myVar);
             };
            render: function () {
                 return (
                    <div className="component">
                        {this.state.position}
                    </div>
                 ); 
            }

        });

ReactDOM.render(
    <Component />,
    document.getElementById('main')
);
Khalid Azam
fonte
11

Eu sei que isso é um pouco antigo, mas é importante notar que o React recomenda limpar o intervalo de desmontagem do componente: https://reactjs.org/docs/state-and-lifecycle.html

Então, eu gosto de adicionar esta resposta a esta discussão:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
Fernando lopes
fonte
8

setStateestá sendo invocado imediatamente devido ao parêntese! Envolva-o em uma função anônima e chame-o:

setTimeout(function() {
    this.setState({position: 1})
}.bind(this), 3000);
tymeJV
fonte
6

Você não disse quem chamou setTimeout

Aqui está como você chama o tempo limite sem chamar funções adicionais.

1. Você pode fazer isso sem criar funções adicionais.

setTimeout(this.setState.bind(this, {position:1}), 3000);

Usa function.prototype.bind ()

setTimeout pega a localização da função e a mantém no contexto.

2. Outra maneira de fazer o mesmo escrevendo ainda menos código.

setTimeout(this.setState, 3000, {position:1});

Provavelmente usa o mesmo método de ligação em algum ponto

O setTimeout leva apenas a localização da função e a função já tem o contexto? Enfim, funciona!

NOTA: Funcionam com qualquer função usada no js.

Aconselhar
fonte
5

Seu escopo de código ( this) será seu windowobjeto, não seu componente de reação, e é por isso setTimeout(this.setState({position: 1}), 3000)que travará dessa maneira.

Isso vem de javascript não React, é js closure


Portanto, para vincular o escopo do componente de reação atual, faça o seguinte:

setTimeout(function(){this.setState({position: 1})}.bind(this), 3000);

Ou se o seu navegador suportar es6 ou seu projs tiver suporte para compilar es6 a es5, tente a função de seta também, pois a função de seta é para corrigir 'este' problema:

setTimeout(()=>this.setState({position: 1}), 3000);
Xin
fonte
3

Existem 3 maneiras de acessar o escopo dentro da função 'setTimeout'

Primeiro,

const self = this
setTimeout(function() {
  self.setState({position:1})
}, 3000)

A segunda é usar a função de seta ES6, porque a função de seta não tinha escopo próprio

setTimeout(()=> {
   this.setState({position:1})
}, 3000)

O terceiro é vincular o escopo dentro da função

setTimeout(function(){
   this.setState({position:1})
}.bind(this), 3000)
Darryl Fabian
fonte
1

Você fez um erro de declaração de sintaxe, use a declaração setTimeout adequada

message:() => { 
  setTimeout(() => {this.setState({opened:false})},3000); 
  return 'Thanks for your time, have a nice day 😊! 
}
KARTHIKEYAN.A
fonte