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?

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

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

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.


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

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

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.


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>;
  }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.