ReactJS: setTimeout () non funziona?


100

Tenendo presente questo codice:

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')
);

Lo stato non dovrebbe cambiare solo dopo 3 secondi? Sta cambiando immediatamente.

Il mio obiettivo principale qui è cambiare lo stato ogni 3 secondi (con setInterval()), ma poiché non funzionava, ho provato setTimeout(), il che non funziona neanche. Qualche luce su questo? Grazie!


2
Se si dispone foo(bar())poi barviene eseguito per primo e il suo valore di ritorno è passato a foo.
Felix Kling

@FelixKling sembra corretto, ma non appropriato. Poiché il foo()qui è esattamente da eseguire bardopo il timeout desiderato. O sono completamente sbagliato e viene eseguito immediatamente e restituisce il valore solo dopo il tempo desiderato?
jbarradas

3
"Dato che foo () qui serve esattamente per eseguire la barra dopo il timeout desiderato." Bene, ecco perché devi passare bar, non chiamarlo e passare il suo valore di ritorno. Ti aspettavi che il comportamento di foo(bar())cambiasse, a seconda di cosa foosta facendo? Sarebbe davvero strano.
Felix Kling

Risposte:


241

Fare

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

Altrimenti, stai passando il risultato di setStatea setTimeout.

È inoltre possibile utilizzare le funzioni freccia di ES6 per evitare l'uso della thisparola chiave:

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

1
Sì, ha senso e funziona. Ma function () non è una funzione? Allora perché dovremmo vincolarlo? Ho già provato ed è davvero necessario, volevo solo sapere perché.
Apprezzo il

Non capisco perché stai dicendo che passerebbe il risultato a setTimeout, come fa a non farlo funzionare? Qual è il comportamento in quel caso?
PositiveGuy

16
per quelli di voi che preferiscono usare le funzioni freccia ES6: setTimeout(() => {this.setState({ position: 1 })}, 3000)@PositiveGuy non è sicuro di averlo fatto da solo da quando è stata pubblicata questa domanda, ma nel caso in cui non l'hai fatto: l'esempio originale di Daniel deve .bind(this)limitare il thiscontesto a setState- altrimenti , thisfarà automaticamente riferimento al contesto in cui viene invocato (in questo caso, l'anonimo a cui functionviene passato setTimeout). Le funzioni freccia ES6, tuttavia, hanno ambito lessicale : si limitano thisal contesto in cui vengono chiamate.
Zac Collier

1
Non funziona ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('Will we Happening?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Aggiungi nuova organizzazione'});}}, 100); È nel contesto della classe sintattica della classe sugar Le organizzazioni estendono Component {console.log never gets console.log ('Will we going?'); Tutto prima e dopo che viene registrato.
juslintek

@juslintek definisce non funziona. si prega di fare una nuova domanda, se necessario.
Daniel A. White

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

Quanto sopra funzionerebbe anche perché la funzione freccia ES6 non cambia il contesto di this.


3
La sintassi ES6 dovrebbe essere la risposta accettata per le migliori pratiche in React. Entrambi funzioneranno, ma questo è più elegante e maniglie this.
mccambridge

24

Ogni volta che creiamo un timeout, dovremmo cancellarlo su componentWillUnmount, se non è ancora stato attivato.

      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')
);

11

So che questo è un po 'vecchio, ma è importante notare che React consiglia di cancellare l'intervallo quando il componente si smonta: https://reactjs.org/docs/state-and-lifecycle.html

Quindi mi piace aggiungere questa risposta a questa discussione:

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

8

setStateviene invocato immediatamente a causa delle parentesi! Avvolgilo in una funzione anonima, quindi chiamalo:

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

6

Non hai detto chi ha chiamato setTimeout

Ecco come chiamare il timeout senza chiamare funzioni aggiuntive.

1. Puoi farlo senza creare funzioni aggiuntive.

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

Utilizza function.prototype.bind ()

setTimeout prende la posizione della funzione e la mantiene nel contesto.

2. Un altro modo per fare lo stesso anche scrivendo ancora meno codice.

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

Probabilmente ad un certo punto utilizza lo stesso metodo di associazione

Il setTimeout prende solo la posizione della funzione e la funzione ha già il contesto? Comunque funziona!

NOTA: funzionano con qualsiasi funzione utilizzata in js.


5

Il tuo codice scope ( this) sarà il tuo windowoggetto, non il tuo componente di reazione, ed è per questosetTimeout(this.setState({position: 1}), 3000) andrà in crash in questo modo.

Viene da javascript non da React, è chiusura js


Quindi, per vincolare l'ambito del tuo attuale componente di reazione, fai questo:

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

Oppure se il tuo browser supporta es6 o i tuoi progetti supportano la compilazione da es6 a es5, prova anche la funzione freccia, poiché la funzione freccia serve a risolvere "questo" problema:

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

3

Esistono 3 modi per accedere all'ambito all'interno della funzione "setTimeout"

Primo,

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

Il secondo è usare la funzione freccia ES6, perché la funzione freccia non aveva l'ambito (questo)

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

Il terzo è vincolare l'ambito all'interno della funzione

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

1

Hai fatto un errore di dichiarazione di sintassi, usa la dichiarazione setTimeout corretta

message:() => { 
  setTimeout(() => {this.setState({opened:false})},3000); 
  return 'Thanks for your time, have a nice day 😊! 
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.