Aktualisieren Sie die React-Komponente jede Sekunde

114

Ich habe mit React herumgespielt und habe die folgende Zeitkomponente Date.now(), die nur auf dem Bildschirm angezeigt wird:

import React, { Component } from 'react';

class TimeComponent extends Component {
  constructor(props){
    super(props);
    this.state = { time: Date.now() };
  }
  render(){
    return(
      <div> { this.state.time } </div>
    );
  }
  componentDidMount() {
    console.log("TimeComponent Mounted...")
  }
}

export default TimeComponent;

Was wäre der beste Weg, um diese Komponente jede Sekunde zu aktualisieren, um die Zeit aus der Perspektive von React neu zu zeichnen?

Schneeflockenkiller
quelle

Antworten:

150

Sie müssen verwenden setInterval, um die Änderung auszulösen, aber Sie müssen auch den Timer löschen, wenn die Komponente ausgehängt wird, um zu verhindern, dass Fehler zurückbleiben und Speicher verloren geht:

componentDidMount() {
  this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
  clearInterval(this.interval);
}
Waiski
quelle
2
Wenn Sie eine Bibliothek möchten, die solche Dinge zusammenfasst, habe ichreact-interval-rerender
Andy
Ich habe meine setInterval-Methode in meinen Konstruktor eingefügt und das hat besser funktioniert.
Aqteifan
89

Der folgende Code ist ein modifiziertes Beispiel von der React.js-Website.

Der Originalcode ist hier verfügbar: https://reactjs.org/#a-simple-component

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      seconds: parseInt(props.startTimeInSeconds, 10) || 0
    };
  }

  tick() {
    this.setState(state => ({
      seconds: state.seconds + 1
    }));
  }

  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  formatTime(secs) {
    let hours   = Math.floor(secs / 3600);
    let minutes = Math.floor(secs / 60) % 60;
    let seconds = secs % 60;
    return [hours, minutes, seconds]
        .map(v => ('' + v).padStart(2, '0'))
        .filter((v,i) => v !== '00' || i > 0)
        .join(':');
  }

  render() {
    return (
      <div>
        Timer: {this.formatTime(this.state.seconds)}
      </div>
    );
  }
}

ReactDOM.render(
  <Timer startTimeInSeconds="300" />,
  document.getElementById('timer-example')
);
Mr. Polywhirl
quelle
1
Ich erhalte aus irgendeinem Grund this.updater.enqueueSetState ist kein Funktionsfehler, wenn dieser Ansatz verwendet wird. Der Rückruf von setInterval ist ordnungsgemäß an die Komponente this gebunden.
Baldrs
15
Für Idioten wie mich: updater
Nenne
3
Mit ES6 muss ich eine Zeile wie diese ändern.interval = setInterval (this.tick.bind (this), 1000);
Kapil
Können Sie den Link zu der URL hinzufügen, auf die verwiesen wird? Danke ~
frederj
Ich habe eine Formatierungsmethode und eine "Konstruktor" -Eigenschaft hinzugefügt, um die Robustheit zu erhöhen.
Mr. Polywhirl
15

@ Waisky schlug vor:

Sie müssen verwenden setInterval, um die Änderung auszulösen, aber Sie müssen auch den Timer löschen, wenn die Komponente ausgehängt wird, um zu verhindern, dass Fehler zurückbleiben und Speicher verloren geht:

Wenn Sie dasselbe tun möchten, verwenden Sie Hooks:

const [time, setTime] = useState(Date.now());

useEffect(() => {
  const interval = setInterval(() => setTime(Date.now()), 1000);
  return () => {
    clearInterval(interval);
  };
}, []);

Zu den Kommentaren:

Sie müssen nichts hinein geben []. Wenn Sie timedie Klammern übergeben, bedeutet dies, dass der Effekt jedes Mal ausgeführt wird, wenn sich der Wert von timeÄnderungen ändert, dh setIntervaljedes Mal, wenn sich timeÄnderungen ändern, wird eine neue aufgerufen, was nicht das ist, wonach wir suchen. Wir wollen nur aufrufen , setIntervaleinmal , wenn das Bauteil montiert wird und dann setIntervalruft setTime(Date.now())alle 1000 Sekunden. Schließlich rufen wir auf, clearIntervalwenn die Komponente nicht gemountet ist.

Beachten Sie, dass die Komponente timejedes Mal aktualisiert wird, je nachdem, wie Sie sie verwendet haben, wenn sich der Wert von timeändert. Das hat nichts damit zu tun , setzen timein []der useEffect.

1man
quelle
1
Warum haben Sie ein leeres Array ( []) als zweites Argument übergeben useEffect?
Mekeor Melire
In der React-Dokumentation zum Effekt-Hook heißt es : „Wenn Sie einen Effekt ausführen und nur einmal bereinigen möchten (beim Ein- und Aushängen), können Sie ein leeres Array ([]) als zweites Argument übergeben.“ OP möchte jedoch, dass der Effekt jede Sekunde ausgeführt wird, dh erneut auftritt, dh nicht nur einmal.
Mekeor Melire
Der Unterschied, den ich bemerkt habe, wenn Sie [Zeit] im Array übergeben, wird die Komponente in jedem Intervall erstellen und zerstören. Wenn Sie das leere Array [] übergeben, wird die Komponente weiterhin aktualisiert und erst dann ausgehängt, wenn Sie die Seite verlassen. In beiden Fällen musste ich jedoch key = {time} oder key = {Date.now ()} hinzufügen, damit es tatsächlich erneut gerendert werden konnte, damit es funktioniert.
Teebu
8

In der componentDidMountLebenszyklusmethode der Komponente können Sie ein Intervall festlegen, um eine Funktion aufzurufen, die den Status aktualisiert.

 componentDidMount() {
      setInterval(() => this.setState({ time: Date.now()}), 1000)
 }
erik-sn
quelle
4
Richtig, aber wie in der oberen Antwort angegeben, denken Sie daran, die Zeit zu löschen, um Speicherverluste zu vermeiden: componentWillUnmount () {clearInterval (this.interval); }
Alexander Falk
3
class ShowDateTime extends React.Component {
   constructor() {
      super();
      this.state = {
        curTime : null
      }
    }
    componentDidMount() {
      setInterval( () => {
        this.setState({
          curTime : new Date().toLocaleString()
        })
      },1000)
    }
   render() {
        return(
          <div>
            <h2>{this.state.curTime}</h2>
          </div>
        );
      }
    }
Jagadeesh
quelle
0

Sie waren also auf dem richtigen Weg. In Ihrem Unternehmen hätten componentDidMount()Sie den Job durch Implementieren setInterval()beenden können, um die Änderung auszulösen. Denken Sie jedoch daran, dass die Aktualisierung eines Komponentenzustands über setState()erfolgt. In Ihrem componentDidMount()System hätten Sie also Folgendes tun können:

componentDidMount() {
  setInterval(() => {
   this.setState({time: Date.now()})    
  }, 1000)
}

Sie verwenden auch, Date.now()was funktioniert, mit der componentDidMount()Implementierung, die ich oben angeboten habe, aber Sie werden eine lange Reihe von bösen Zahlen aktualisieren, die nicht von Menschen lesbar sind, aber es ist technisch gesehen die Zeit, die seit dem 1. Januar 1970 jede Sekunde in Millisekunden aktualisiert wird, aber wir möchten diese Zeit lesbar machen, wie wir Menschen die Zeit lesen, also setIntervalmöchten Sie zusätzlich zum Lernen und Implementieren etwas darüber lernen new Date()und toLocaleTimeString()und Sie würden es so implementieren:

class TimeComponent extends Component {
  state = { time: new Date().toLocaleTimeString() };
}

componentDidMount() {
  setInterval(() => {
   this.setState({ time: new Date().toLocaleTimeString() })    
  }, 1000)
}

Beachten Sie, dass ich die constructor()Funktion auch entfernt habe . Sie benötigen sie nicht unbedingt. Mein Refactor entspricht zu 100% der Initialisierung der Site mit der constructor()Funktion.

Daniel
quelle
0

Aufgrund von Änderungen in React V16, in denen componentWillReceiveProps () veraltet ist, verwende ich diese Methode zum Aktualisieren einer Komponente. Beachten Sie, dass sich das folgende Beispiel in Typescript befindet und die statische Methode getDerivedStateFromProps verwendet, um den Anfangsstatus und den aktualisierten Status abzurufen, wenn die Requisiten aktualisiert werden.

    class SomeClass extends React.Component<Props, State> {
  static getDerivedStateFromProps(nextProps: Readonly<Props>): Partial<State> | null {
    return {
      time: nextProps.time
    };
  }

  timerInterval: any;

  componentDidMount() {
    this.timerInterval = setInterval(this.tick.bind(this), 1000);
  }

  tick() {
    this.setState({ time: this.props.time });
  }

  componentWillUnmount() {
    clearInterval(this.timerInterval);
  }

  render() {
    return <div>{this.state.time}</div>;
  }
}
Jarora
quelle