Perbarui komponen React setiap detik

114

Saya telah bermain-main dengan React dan memiliki komponen waktu berikut yang baru saja ditampilkan Date.now()di layar:

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;

Apa cara terbaik agar komponen ini diperbarui setiap detik untuk menggambar ulang waktu dari perspektif React?

pembunuh kepingan salju
sumber

Jawaban:

152

Anda perlu menggunakan setIntervaluntuk memicu perubahan, tetapi Anda juga perlu menghapus pengatur waktu saat komponen dilepas untuk mencegahnya meninggalkan kesalahan dan membocorkan memori:

componentDidMount() {
  this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
  clearInterval(this.interval);
}
Waiski
sumber
2
Jika Anda ingin perpustakaan yang merangkum hal semacam ini, saya membuatreact-interval-rerender
Andy
Saya menambahkan metode setInterval saya di konstruktor saya dan itu bekerja lebih baik.
aqteifan
89

Kode berikut adalah contoh modifikasi dari situs web React.js.

Kode asli tersedia di sini: 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')
);
Tuan Polywhirl
sumber
1
Saya untuk beberapa alasan mendapatkan this.updater.enqueueSetState bukanlah kesalahan fungsi saat menggunakan pendekatan ini. Callback setInterval terikat dengan benar ke komponen ini.
baldrs
16
Untuk idiot seperti saya: jangan updater
beri
3
Dengan ES6 saya perlu mengubah satu baris seperti this.interval = setInterval (this.tick.bind (this), 1000);
Kapil
Bisakah Anda menambahkan tautan ke url yang mereferensikan? Terima kasih ~
frederj
Saya menambahkan metode pemformatan dan properti "konstruktor" agar lebih kokoh.
Tn. Polywhirl
15

@Waisky menyarankan:

Anda perlu menggunakan setIntervaluntuk memicu perubahan, tetapi Anda juga perlu menghapus pengatur waktu saat komponen dilepas untuk mencegahnya meninggalkan kesalahan dan membocorkan memori:

Jika Anda ingin melakukan hal yang sama, menggunakan Hooks:

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

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

Mengenai komentar:

Anda tidak perlu memasukkan apapun ke dalam []. Jika Anda mengirimkan timetanda kurung, itu berarti menjalankan efek setiap kali nilai timeperubahan, yaitu, memanggil yang baru setIntervalsetiap kali, timeberubah, yang bukan yang kita cari. Kami hanya ingin memanggil setIntervalsekali ketika komponen dipasang dan kemudian setIntervalmemanggil setTime(Date.now())setiap 1000 detik. Akhirnya, kami memanggil clearIntervalsaat komponen dilepas.

Perhatikan bahwa komponen diperbarui, berdasarkan cara Anda menggunakannya time, setiap kali nilai timeberubah. Yang tidak ada hubungannya dengan menempatkan timedi []dari useEffect.

1man
sumber
1
Mengapa Anda melewatkan array kosong ( []) sebagai argumen kedua useEffect?
Mekeor Melire
The Bereaksi dokumentasi pada efek kait memberitahu kita: “Jika Anda ingin menjalankan efek dan membersihkannya hanya sekali (pada gunung dan unmount), Anda dapat melewati array kosong ([]) sebagai argumen kedua.” Tapi OP ingin efeknya berjalan setiap detik, yaitu reoccurring, yaitu tidak hanya sekali.
Mekeor Melire
Perbedaan yang saya perhatikan jika Anda melewatkan [waktu] dalam array, itu akan membuat dan menghancurkan komponen setiap interval. Jika Anda meneruskan larik kosong [], itu akan terus menyegarkan komponen dan hanya melepasnya saat Anda meninggalkan laman. Namun, dalam kedua kasus tersebut, untuk membuatnya bekerja, saya harus menambahkan key = {time} atau key = {Date.now ()} untuk membuatnya benar-benar dirender.
Teebu
8

Dalam componentDidMountmetode siklus hidup komponen , Anda bisa menyetel interval untuk memanggil fungsi yang memperbarui status.

 componentDidMount() {
      setInterval(() => this.setState({ time: Date.now()}), 1000)
 }
erik-sn
sumber
4
Benar, tetapi seperti yang disarankan oleh jawaban teratas, ingatlah untuk menghapus waktu untuk menghindari kebocoran memori: 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
sumber
0

Jadi Anda berada di jalur yang benar. Di dalam Anda, componentDidMount()Anda dapat menyelesaikan pekerjaan dengan menerapkan setInterval()untuk memicu perubahan, tetapi ingat cara untuk memperbarui status komponen adalah melalui setState(), jadi di dalam Anda, componentDidMount()Anda dapat melakukan ini:

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

Juga, Anda menggunakan Date.now()yang berfungsi, dengan componentDidMount()implementasi yang saya tawarkan di atas, tetapi Anda akan mendapatkan serangkaian panjang pembaruan angka-angka buruk yang tidak dapat dibaca manusia, tetapi secara teknis waktu memperbarui setiap detik dalam milidetik sejak 1 Januari 1970, tetapi kami ingin membuat waktu ini mudah dibaca sebagaimana kita manusia membaca waktu, jadi selain belajar dan menerapkan setIntervalAnda ingin belajar tentang new Date()dan toLocaleTimeString()dan Anda akan menerapkannya seperti ini:

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

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

Perhatikan saya juga menghapus constructor()fungsinya, Anda tidak perlu itu, refactor saya 100% setara dengan menginisialisasi situs dengan constructor()fungsi tersebut.

Daniel
sumber
0

Karena perubahan pada React V16 di mana componentWillReceiveProps () tidak digunakan lagi, ini adalah metodologi yang saya gunakan untuk memperbarui komponen. Perhatikan bahwa contoh di bawah ini ada di Typecript dan menggunakan metode getDerivedStateFromProps statis untuk mendapatkan status awal dan status yang diperbarui setiap kali Alat Peraga diperbarui.

    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
sumber