React bileşenini her saniye güncelleyin


114

React ile oynuyordum Date.now()ve ekranda sadece gösterilen aşağıdaki zaman bileşenine sahibim :

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;

React perspektifinden zamanı yeniden çekmek için bu bileşenin her saniye güncellenmesini sağlamanın en iyi yolu nedir?

Yanıtlar:


152

setIntervalDeğişikliği tetiklemek için kullanmanız gerekir , ancak bileşen ayrıldığında hata bırakmasını ve bellek sızmasını önlemek için zamanlayıcıyı da temizlemeniz gerekir:

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

2
Bu tür şeyleri kapsayan bir kütüphane istiyorsanız, hazırladımreact-interval-rerender
Andy

SetInterval yöntemimi kurucuma ekledim ve bu daha iyi çalıştı.
aqteifan

89

Aşağıdaki kod, React.js web sitesinden değiştirilmiş bir örnektir.

Orijinal kod burada mevcuttur: 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
Ben bazı nedenlerden dolayı this.updater.enqueueSetState bu yaklaşımı kullanırken bir işlev hatası değil olsun. setInterval geri araması, bu bileşene düzgün bir şekilde bağlıdır.
baldrs

16
Benim gibi aptallar için: Zamanlayıcıyı updateradlandırmayın çünkü güncelleme döngüsünü mahvediyor
baldrs

3
ES6 ile bunun gibi bir satırı değiştirmem gerekiyor. İnterval = setInterval (this.tick.bind (this), 1000);
Kapil

Referans veren url'ye bağlantı ekleyebilir misiniz? Teşekkürler ~
frederj

Daha sağlamlık için bir biçimlendirme yöntemi ve bir "yapıcı" özelliği ekledim.
Bay Polywhirl

15

@Waisky önerdi:

setIntervalDeğişikliği tetiklemek için kullanmanız gerekir , ancak bileşen ayrıldığında hata bırakmasını ve bellek sızmasını önlemek için zamanlayıcıyı da temizlemeniz gerekir:

Aynı şeyi Hook'ları kullanarak yapmak isterseniz:

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

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

Yorumlarla ilgili olarak:

İçeriye hiçbir şey vermenize gerek yok []. timeParantez içinde geçerseniz , bu, timedeğişimin değeri her seferinde etkiyi çalıştırmanız anlamına gelir , yani setIntervalher seferinde yeni bir timedeğişikliği çağırır , bu bizim aradığımız şey değildir. Biz sadece çağırmak istediğiniz setIntervalbileşeni monte aldığında kez ve sonra setIntervalçağıran setTime(Date.now())her 1000 saniyede. Son olarak, clearIntervalbileşen kaldırıldığında çağırırız.

timeDeğeri her timedeğiştiğinde , içinde nasıl kullandığınıza bağlı olarak bileşenin güncellendiğini unutmayın . Yani koyarak ile ilgisi yoktur timeiçinde []bir useEffect.


1
Neden []ikinci argüman olarak boş bir dizi ( ) ilettiniz useEffect?
Mekeor Melire

Etki kanca dokümantasyon tepki söyler: “Eğer, ikinci bir argüman olarak boş bir dizi ([]) geçebileceği bir etkiye çalıştırın ve (bağlama ve bağlantısını kesme üzerine) sadece bir kez bunu temizlemek istiyorum.” Ancak OP, etkinin her saniye, yani tekrar eden, yani sadece bir kez değil, çalışmasını ister.
Mekeor Melire

Fark ettiğim fark, dizide [zaman] 'ı geçirirseniz, her aralıkta bileşeni oluşturur ve yok eder. Boş diziyi [] geçerseniz, bileşeni yenilemeye devam eder ve yalnızca sayfadan ayrıldığınızda bağlantısını keser. Ancak, her iki durumda da, çalışmasını sağlamak için, gerçekten yeniden oluşturmasını sağlamak için anahtar = {saat} veya anahtar = {Date.now ()} eklemem gerekiyordu.
Teebu

8

Bileşenin componentDidMountyaşam döngüsü yönteminde, durumu güncelleyen bir işlevi çağırmak için bir aralık ayarlayabilirsiniz.

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

4
Doğru, ancak en iyi yanıtın da önerdiği gibi, bellek sızıntısını önlemek için zamanı temizlemeyi unutmayın: 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

Yani doğru yoldaydın. İçerideki componentDidMount()Eğer uygulayarak işi bitmiş olabilirdi setInterval()devlet ile gönderildiği bileşenleri güncelleştirmek için bir yol değişikliğini tetikleyen, ancak şunu unutmayın setState()bu yüzden içeride, senin componentDidMount()bunu yapmış olabilir:

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

Ayrıca, yukarıda sunduğum uygulamayla Date.now()hangisinin işe yaradığını kullanıyorsunuz componentDidMount(), ancak insan tarafından okunamayan uzun bir kötü sayılar dizisi alacaksınız, ancak teknik olarak 1 Ocak 1970'ten bu yana milisaniye cinsinden her saniyeyi güncelleme zamanı, ancak biz böylece öğrenme ve uygulamaya ek olarak, biz okuma zamanını insanlarda nasıl bu kez okunabilir yapmak istiyorum setIntervalhakkında öğrenmek istediğiniz new Date()ve toLocaleTimeString()ve o kadar gibi uygulamaya koyacak:

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

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

Ayrıca constructor()fonksiyonu kaldırdım, buna ihtiyacınız yok, benim refaktörüm siteyi constructor()fonksiyonla başlatmaya% 100 eşdeğer .


0

ComponentWillReceiveProps () uygulamasının kullanımdan kaldırıldığı React V16'daki değişiklikler nedeniyle, bir bileşeni güncellemek için kullandığım metodoloji budur. Aşağıdaki örneğin Typescript içinde olduğuna ve Props her güncellendiğinde ilk durumu ve güncellenen durumu almak için statik getDerivedStateFromProps yöntemini kullandığına dikkat edin.

    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>;
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.