setInterval in einer React-App

100

Ich bin noch ziemlich neu bei React, aber ich habe mich langsam weiterentwickelt und bin auf etwas gestoßen, an dem ich festgefahren bin.

Ich versuche, eine "Timer" -Komponente in React zu erstellen, und um ehrlich zu sein, weiß ich nicht, ob ich das richtig (oder effizient) mache. In meinem Code unten habe ich den Status so festgelegt, dass er ein Objekt { currentCount: 10 }zurückgibt und mit dem er gespielt componentDidMounthat componentWillUnmount, renderund ich kann den Status nur dazu bringen, von 10 auf 9 "herunterzuzählen".

Zweiteilige Frage: Was mache ich falsch? Und gibt es eine effizientere Möglichkeit, setTimeout zu verwenden (anstatt componentDidMount& componentWillUnmount)?

Vielen Dank im Voraus.

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;
Jose
quelle
2
bind(this)wird nicht mehr benötigt, reagieren macht das jetzt alleine.
Derek Pollard
2
Ihre Timer-Methode aktualisiert currentCount
Bryan Chen
1
@Derek bist du sicher? Ich habe gerade meine Arbeit durch Hinzufügen this.timer.bind(this)als this.timer allein hat nicht funktioniert
Der Wurm
6
@Theworm @Derek ist irgendwie falsch. React.createClass (das veraltet ist) bindet Methoden class Clock extends Componentautomatisch, bindet sie jedoch nicht automatisch. Es hängt also davon ab, wie Sie Ihre Komponenten erstellen, ob Sie binden müssen.
CallMeNorm

Antworten:

154

Ich sehe 4 Probleme mit Ihrem Code:

  • In Ihrer Timer-Methode setzen Sie Ihre aktuelle Anzahl immer auf 10
  • Sie versuchen, den Status in der Rendermethode zu aktualisieren
  • Sie verwenden keine setStateMethode, um den Status tatsächlich zu ändern
  • Sie speichern Ihre Intervall-ID nicht im Status

Versuchen wir das zu beheben:

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

Dies würde zu einem Timer führen, der von 10 auf -N abnimmt. Wenn Sie einen Timer wünschen, der auf 0 sinkt, können Sie eine leicht modifizierte Version verwenden:

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},
dotnetom
quelle
Danke dir. Das macht sehr viel Sinn. Ich bin immer noch ein Anfänger und ich versuche herauszufinden, wie der Zustand funktioniert und was in welchen "Brocken" steckt, wie beim Rendern.
Jose
Ich frage mich jedoch, ob es erforderlich ist, componentDidMount und componentWillUnmount zu verwenden, um das Intervall tatsächlich festzulegen. BEARBEITEN: Ich habe gerade Ihre letzte Bearbeitung gesehen. :)
Jose
@Jose Ich denke, es componentDidMountist der richtige Ort, um die clientseitigen Ereignisse auszulösen, also würde ich es verwenden, um den Countdown einzuleiten. Über welche andere Methode denken Sie beim Initialisieren nach?
Dotnetom
Ich hatte nichts Besonderes im Sinn, aber es schien klobig, so viele "Brocken" in einer Komponente zu verwenden. Ich nehme an, ich gewöhne mich nur daran, wie die Teile in React funktionieren. Nochmals vielen Dank!
Jose
4
Es ist nicht wirklich notwendig, den setInterval-Wert als Teil des Status zu speichern, da er das Rendering nicht beeinflusst
Gil
32

Aktualisierter 10-Sekunden-Countdown mit 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
quelle
20

Aktualisierter 10-Sekunden-Countdown mithilfe von Hooks (ein neuer Funktionsvorschlag, mit dem Sie Status- und andere React-Funktionen verwenden können, ohne eine Klasse zu schreiben. Sie befinden sich derzeit in 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
quelle
Mit React 16.8 sind React Hooks in einer stabilen Version verfügbar.
Greg Herbowicz
2

Danke @dotnetom, @ greg-herbowicz

Wenn "this.state is undefined" zurückgegeben wird, binden Sie die Timer-Funktion:

constructor(props){
    super(props);
    this.state = {currentCount: 10}
    this.timer = this.timer.bind(this)
}
Tulsluper
quelle
2

Wenn jemand nach einem React Hook-Ansatz zur Implementierung von setInterval sucht. Dan Abramov hat in seinem Blog darüber gesprochen . Probieren Sie es aus, wenn Sie eine gute Lektüre über das Thema einschließlich eines Klassenansatzes wünschen. Grundsätzlich ist der Code ein benutzerdefinierter Hook, der setInterval deklarativ macht.

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]);
}

Veröffentlichen Sie der Einfachheit halber auch den CodeSandbox-Link: https://codesandbox.io/s/105x531vkq

Jo E.
quelle
0

Aktualisieren des Status jede Sekunde in der Reaktionsklasse. Beachten Sie, dass my index.js eine Funktion übergibt, die die aktuelle Zeit zurückgibt.

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
quelle