ReactJS: setTimeout () ne fonctionne pas?

100

Ayant ce code à l'esprit:

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

L'état n'est-il pas censé changer seulement après 3 secondes? Cela change immédiatement.

Mon objectif principal ici est de changer l'état toutes les 3 secondes (avec setInterval()), mais comme cela ne fonctionnait pas, j'ai essayé setTimeout(), ce qui ne fonctionne pas non plus. Des lumières à ce sujet? Merci!

jbarradas
la source
2
Si vous avez foo(bar())alors barest exécuté en premier et sa valeur de retour est transmise à foo.
Felix Kling
@FelixKling qui semble correct, mais pas approprié. Puisque le foo()ici est exactement à exécuter baraprès le délai d'expiration souhaité. Ou ai-je complètement tort et il s'exécute tout de suite, et ne renvoie la valeur qu'après l'heure souhaitée?
jbarradas
3
"Puisque le toto () est ici exactement pour exécuter la barre après le délai d'expiration souhaité." Bon, c'est pourquoi vous devez passer bar, ne pas l'appeler et transmettre sa valeur de retour. Vous attendiez-vous à ce que le comportement de foo(bar())change, en fonction defoo se passe? Ce serait vraiment étrange.
Felix Kling

Réponses:

241

Faire

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

Sinon, vous passez le résultat de setStateà setTimeout.

Vous pouvez également utiliser les fonctions fléchées ES6 pour éviter l'utilisation du thismot-clé:

setTimeout(
  () => this.setState({ position: 1 }), 
  3000
);
Daniel A. White
la source
1
Ouais, ça a du sens et ça marche. Mais la fonction () n'est-elle pas une fonction? Alors pourquoi aurions-nous besoin de le lier? J'ai déjà essayé et c'est vraiment nécessaire, je voulais juste savoir pourquoi. Merci pour votre aide :)
jbarradas
Je ne comprends pas pourquoi vous dites qu'il passerait le résultat à setTimeout, comment cela ne fonctionne-t-il pas? Quel est le comportement dans ce cas?
PositiveGuy
16
pour ceux d'entre vous qui préfèrent utiliser les fonctions fléchées ES6: setTimeout(() => {this.setState({ position: 1 })}, 3000)@PositiveGuy ne sait pas si vous avez fait des recherches vous-même depuis que cette question a été publiée, mais au cas où vous ne l'auriez pas fait: l'exemple original de Daniel doit .bind(this)restreindre le thiscontexte à setState- sinon , thisfera automatiquement référence au contexte dans lequel il est invoqué (dans ce cas, l'anonyme functionétant passé à setTimeout). Les fonctions fléchées ES6, cependant, ont une portée lexicale - elles se limitent thisau contexte dans lequel elles sont appelées.
Zac Collier
1
Ne fonctionne pas ... setTimeout (() => {if (! This.props.logoIsLoading &&! This.props.isLoading) {console.log ('Cela va-t-il arriver?'); This.setState ({.. .this.state, shouldUpdate: false, itemToUpdate: null, modalIsOpen: false, modalTitle: 'Ajouter une nouvelle organisation'});}}, 100); C'est dans le contexte de la classe syntaxique de la classe de sucre Organisations étend Component {console.log n'obtient jamais console.log ('Va-t-on arriver?'); Tout avant et après il est enregistré.
juslintek
@juslintek définit ne pas fonctionner. veuillez poser une nouvelle question si nécessaire.
Daniel A. White
150
setTimeout(() => {
  this.setState({ position: 1 });
}, 3000);

Ce qui précède fonctionnerait également car la fonction de flèche ES6 ne change pas le contexte de this.

Steven Scaffidi
la source
3
La syntaxe ES6 devrait être la réponse acceptée pour les meilleures pratiques dans React. Les deux fonctionneront, mais c'est plus élégant et poignées this.
mccambridge
24

Chaque fois que nous créons un timeout, nous devons l'effacer sur componentWillUnmount, s'il n'a pas encore été déclenché.

      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
la source
11

Je sais que c'est un peu vieux, mais il est important de noter que React recommande d'effacer l'intervalle lorsque le composant se démonte: https://reactjs.org/docs/state-and-lifecycle.html

J'aime donc ajouter cette réponse à cette discussion:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
Fernando Lopes
la source
8

setStateest appelé immédiatement en raison de la parenthèse! Enveloppez-le dans une fonction anonyme, puis appelez-le:

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

Vous n'avez pas dit qui a appelé setTimeout

Voici comment appeler timeout sans appeler de fonctions supplémentaires.

1. Vous pouvez le faire sans effectuer de fonctions supplémentaires.

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

Utilise function.prototype.bind ()

setTimeout prend l'emplacement de la fonction et la conserve dans le contexte.

2. Une autre façon de faire la même chose même en écrivant encore moins de code.

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

Utilise probablement la même méthode de liaison à un moment donné

Le setTimeout prend uniquement l'emplacement de la fonction et la fonction a déjà le contexte? Quoi qu'il en soit, ça marche!

REMARQUE: ils fonctionnent avec toutes les fonctions que vous utilisez dans js.

Conseiller
la source
5

Votre portée de code ( this) sera votre windowobjet, pas votre composant de réaction, et c'est pourquoisetTimeout(this.setState({position: 1}), 3000) se plantera de cette façon.

Cela vient de javascript pas de React, c'est la fermeture de js


Donc, afin de lier votre portée actuelle du composant react, procédez comme suit:

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

Ou si votre navigateur prend en charge es6 ou que vos projets prennent en charge la compilation de es6 en es5, essayez également la fonction arrow, car arrow func est de résoudre ce problème:

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

Il existe 3 façons d'accéder à la portée à l'intérieur de la fonction 'setTimeout'

Première,

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

La deuxième consiste à utiliser la fonction de flèche ES6, car la fonction de flèche n'avait pas de portée (ceci)

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

Le troisième est de lier la portée à l'intérieur de la fonction

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

Vous avez fait une erreur de déclaration de syntaxe, utilisez la déclaration setTimeout appropriée

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