setInterval dans une application React

103

Je suis encore assez nouveau chez React, mais j'ai progressé lentement et j'ai rencontré quelque chose sur lequel je suis coincé.

J'essaie de créer un composant "timer" dans React, et pour être honnête, je ne sais pas si je le fais correctement (ou efficacement). Dans mon code ci - dessous, je mets l'état de retourner un objet { currentCount: 10 }et ont été à jouer avec componentDidMount, componentWillUnmountet renderje peux seulement obtenir l'état de « compte à rebours » de 10 à 9.

Question en deux parties: qu'est-ce que je me trompe? Et, y a-t-il un moyen plus efficace d'utiliser setTimeout (plutôt que d'utiliser componentDidMount& componentWillUnmount)?

Merci d'avance.

import React from 'react';

var Clock = React.createClass({

  getInitialState: function() {
    return { currentCount: 10 };
  },

  componentDidMount: function() {
    this.countdown = setInterval(this.timer, 1000);
  },

  componentWillUnmount: function() {
    clearInterval(this.countdown);
  },

  timer: function() {
    this.setState({ currentCount: 10 });
  },

  render: function() {
    var displayCount = this.state.currentCount--;
    return (
      <section>
        {displayCount}
      </section>
    );
  }

});

module.exports = Clock;
José
la source
2
bind(this)n'est plus nécessaire, react le fait tout seul maintenant.
Derek Pollard
2
votre méthode de minuterie ne met pas à jour currentCount
Bryan Chen
1
@Derek êtes-vous sûr? Je viens de faire fonctionner le mien en ajoutant this.timer.bind(this)que this.timer seul ne fonctionnait pas
Le ver
6
@Theworm @Derek a tort, en quelque sorte. React.createClass (qui est obsolète) associe automatiquement les méthodes, mais class Clock extends Componentne se lie pas automatiquement. Cela dépend donc de la façon dont vous créez vos composants si vous devez lier.
CallMeNorm

Réponses:

160

Je vois 4 problèmes avec votre code:

  • Dans votre méthode de minuterie, vous définissez toujours votre compte actuel sur 10
  • Vous essayez de mettre à jour l'état dans la méthode de rendu
  • Vous n'utilisez pas de setStateméthode pour modifier réellement l'état
  • Vous ne stockez pas votre intervalId dans l'état

Essayons de résoudre ce problème:

componentDidMount: function() {
   var intervalId = setInterval(this.timer, 1000);
   // store intervalId in the state so it can be accessed later:
   this.setState({intervalId: intervalId});
},

componentWillUnmount: function() {
   // use intervalId from the state to clear the interval
   clearInterval(this.state.intervalId);
},

timer: function() {
   // setState method is used to update the state
   this.setState({ currentCount: this.state.currentCount -1 });
},

render: function() {
    // You do not need to decrease the value here
    return (
      <section>
       {this.state.currentCount}
      </section>
    );
}

Il en résulterait une minuterie qui diminue de 10 à -N. Si vous voulez une minuterie qui diminue à 0, vous pouvez utiliser une version légèrement modifiée:

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},
dotnetom
la source
Je vous remercie. Cela a beaucoup de sens. Je suis encore très débutant et j'essaie de comprendre comment fonctionne l'état et ce qui se passe dans quels "morceaux", comme le rendu.
Jose
Je me demande, cependant, est-il nécessaire d'utiliser componentDidMount et componentWillUnmount pour définir réellement l'intervalle? EDIT: Je viens de voir votre dernière modification. :)
Jose
@Jose Je pense que componentDidMountc'est le bon endroit pour déclencher les événements côté client, donc je l'utiliserais pour lancer le compte à rebours. À quelle autre méthode pensez-vous pour l'initialisation?
dotnetom
Je n'avais rien d'autre en particulier en tête, mais il me semblait maladroit d'utiliser autant de "morceaux" dans un composant. Je suppose que c'est juste que je m'habitue à la façon dont les éléments fonctionnent dans React. Encore merci!
Jose
4
Il n'est pas vraiment nécessaire de stocker la valeur setInterval dans le cadre de l'état car cela n'affecte pas le rendu
Gil
32

Compte à rebours de 10 secondes mis à jour avec class Clock extends Component

import React, { Component } from 'react';

class Clock extends Component {
  constructor(props){
    super(props);
    this.state = {currentCount: 10}
  }
  timer() {
    this.setState({
      currentCount: this.state.currentCount - 1
    })
    if(this.state.currentCount < 1) { 
      clearInterval(this.intervalId);
    }
  }
  componentDidMount() {
    this.intervalId = setInterval(this.timer.bind(this), 1000);
  }
  componentWillUnmount(){
    clearInterval(this.intervalId);
  }
  render() {
    return(
      <div>{this.state.currentCount}</div>
    );
  }
}

module.exports = Clock;
Greg Herbowicz
la source
20

Mise à jour du compte à rebours de 10 secondes à l'aide de Hooks (une nouvelle proposition de fonctionnalité qui vous permet d'utiliser l'état et d'autres fonctionnalités de React sans écrire de classe. Ils sont actuellement dans React v16.7.0-alpha).

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

const Clock = () => {
    const [currentCount, setCount] = useState(10);
    const timer = () => setCount(currentCount - 1);

    useEffect(
        () => {
            if (currentCount <= 0) {
                return;
            }
            const id = setInterval(timer, 1000);
            return () => clearInterval(id);
        },
        [currentCount]
    );

    return <div>{currentCount}</div>;
};

const App = () => <Clock />;

ReactDOM.render(<App />, document.getElementById('root'));
Greg Herbowicz
la source
Avec React 16.8, les Hooks React sont disponibles dans une version stable.
Greg Herbowicz
4

Si quelqu'un recherche une approche React Hook pour implémenter setInterval. Dan Abramov en a parlé sur son blog . Vérifiez-le si vous voulez une bonne lecture sur le sujet, y compris une approche de classe. Fondamentalement, le code est un Hook personnalisé qui transforme setInterval comme déclaratif.

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

Publier également le lien CodeSandbox pour plus de commodité: https://codesandbox.io/s/105x531vkq

Jo E.
la source
2

Merci @dotnetom, @ greg-herbowicz

Si elle renvoie "this.state is undefined" - fonction de temporisation de liaison:

constructor(props){
    super(props);
    this.state = {currentCount: 10}
    this.timer = this.timer.bind(this)
}
Tulsluper
la source
0

Mise à jour de l'état toutes les secondes dans la classe react. Notez que my index.js transmet une fonction qui renvoie l'heure actuelle.

import React from "react";

class App extends React.Component {
  constructor(props){
    super(props)

    this.state = {
      time: this.props.time,

    }        
  }
  updateMe() {
    setInterval(()=>{this.setState({time:this.state.time})},1000)        
  }
  render(){
  return (
    <div className="container">
      <h1>{this.state.time()}</h1>
      <button onClick={() => this.updateMe()}>Get Time</button>
    </div>
  );
}
}
export default App;
Ashok Shah
la source