setInterval di aplikasi React

101

Saya masih cukup baru di React, tetapi saya telah bekerja dengan lambat dan saya menemukan sesuatu yang membuat saya terjebak.

Saya mencoba membuat komponen "pengatur waktu" di React, dan sejujurnya saya tidak tahu apakah saya melakukan ini dengan benar (atau efisien). Dalam kode saya di bawah ini, saya mengatur negara untuk mengembalikan objek { currentCount: 10 }dan telah mempermainkan dengan componentDidMount, componentWillUnmount, dan renderdan saya hanya bisa mendapatkan negara untuk "count down" 10-9.

Pertanyaan dua bagian: Apa yang salah? Dan, apakah ada cara yang lebih efisien untuk menggunakan setTimeout (daripada menggunakan componentDidMount& componentWillUnmount)?

Terima kasih sebelumnya.

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
sumber
2
bind(this)tidak lagi diperlukan, bereaksi melakukan ini sendiri sekarang.
Derek Pollard
2
metode pengatur waktu Anda tidak memperbarui currentCount
Bryan Chen
1
@Derek apakah kamu yakin? Saya baru saja membuat milik saya bekerja dengan menambahkan this.timer.bind(this)karena ini. Pengatur waktu itu sendiri tidak berfungsi
Worm
6
@Theworm @Derek salah, semacam. React.createClass (yang sudah tidak digunakan lagi) metode autobind, tetapi class Clock extends Componenttidak mengikat otomatis. Jadi itu tergantung pada bagaimana Anda membuat komponen apakah Anda perlu mengikat.
CallMeNorm

Jawaban:

157

Saya melihat 4 masalah dengan kode Anda:

  • Dalam metode pengatur waktu, Anda selalu menyetel hitungan saat ini ke 10
  • Anda mencoba memperbarui status dalam metode render
  • Anda tidak menggunakan setStatemetode untuk benar-benar mengubah keadaan
  • Anda tidak menyimpan intervalId Anda di negara bagian

Mari coba perbaiki itu:

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

Ini akan menghasilkan pengatur waktu yang berkurang dari 10 ke -N. Jika Anda menginginkan timer yang berkurang menjadi 0, Anda dapat menggunakan versi yang sedikit dimodifikasi:

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},
dotnetom.dll
sumber
Terima kasih. Ini sangat masuk akal. Saya masih sangat pemula dan saya mencoba memahami cara kerja negara dan apa yang ada di "potongan", seperti render.
Jose
Saya bertanya-tanya, apakah perlu menggunakan componentDidMount dan componentWillUnmount untuk benar-benar menyetel intervalnya? EDIT: Baru saja melihat hasil edit terbaru Anda. :)
Jose
@Jose Saya pikir componentDidMountadalah tempat yang tepat untuk memicu peristiwa sisi klien, jadi saya akan menggunakannya untuk memulai hitungan mundur. Metode lain apa yang Anda pikirkan untuk memulai?
dotnetom
Saya tidak memikirkan hal lain secara khusus, tetapi tampaknya kikuk untuk menggunakan begitu banyak "potongan" di dalam sebuah komponen. Saya kira itu hanya diri saya sendiri yang terbiasa dengan cara kerja potongan-potongan di React. Sekali lagi terimakasih!
Jose
4
Tidak ada kebutuhan nyata untuk menyimpan nilai setInterval sebagai bagian dari keadaan karena tidak mempengaruhi rendering
Gil
32

Memperbarui hitungan mundur 10 detik menggunakan 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
sumber
20

Memperbarui hitungan mundur 10 detik menggunakan Hooks (proposal fitur baru yang memungkinkan Anda menggunakan status dan fitur React lainnya tanpa menulis kelas. Saat ini mereka ada di 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
sumber
Dengan React 16.8, React Hooks tersedia dalam rilis stabil.
Greg Herbowicz
2

Terima kasih @dotnetom, @ greg-herbowicz

Jika mengembalikan "this.state is undefined" - mengikat fungsi timer:

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

Jika ada yang mencari pendekatan React Hook untuk mengimplementasikan setInterval. Dan Abramov membicarakannya di blognya . Lihat jika Anda ingin membaca dengan baik tentang subjek termasuk pendekatan Kelas. Pada dasarnya kode ini adalah Hook khusus yang mengubah setInterval sebagai deklaratif.

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

Juga memposting tautan CodeSandbox untuk kenyamanan: https://codesandbox.io/s/105x531vkq

Jo E.
sumber
0

Memperbarui status setiap detik di kelas react. Perhatikan bahwa index.js saya melewati fungsi yang mengembalikan waktu saat ini.

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
sumber