Reactコンポーネントを毎秒更新


114

私はReactをいじっていてDate.now()、画面にレンダリングするだけの次の時間コンポーネントを持っています:

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の観点から時間を再描画する最良の方法は何ですか?

回答:


150

を使用setIntervalして変更をトリガーする必要がありますが、コンポーネントがマウント解除されたときにタイマーをクリアして、エラーが発生したりメモリがリークしたりしないようにする必要もあります。

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

2
この種のものをカプセル化するライブラリが必要な場合は、私が作成しましたreact-interval-rerender
Andy

私は自分のコンストラクタにsetIntervalメソッドを追加し、それがよりうまくいきました。
aqteifan

89

次のコードは、React.js Webサイトからの変更例です。

元のコードはこちらから入手できます: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
何らかの理由で、このアプローチを使用するとthis.updater.enqueueSetStateが関数エラーにならない。setIntervalコールバックは、コンポーネントthisに適切にバインドされています。
2017年

16
私のようなバカの場合:updater更新サイクルを台無しにするので、タイマーに名前を付けないでください
2017年

3
ES6では、this.interval = setInterval(this.tick.bind(this)、1000);のように1行変更する必要があります。
Kapil

参照するURLへのリンクを追加できますか?よろしく
お願いいたします。– frederj

堅牢性を高めるために、フォーマットメソッドと「コンストラクター」プロパティを追加しました。
ポリワール氏

15

@Waiskyは提案しました:

を使用setIntervalして変更をトリガーする必要がありますが、コンポーネントがマウント解除されたときにタイマーをクリアして、エラーが発生したりメモリがリークしたりしないようにする必要もあります。

同じことをしたい場合は、フックを使用します。

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

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

コメントについて:

内部に何も渡す必要はありません[]time角括弧を渡した場合、それはtime変更の値が変更されるたびにエフェクトを実行することを意味します。つまりsetIntervaltime変更されるたびに新しいものを呼び出しますが、これは私たちが求めているものではありません。setIntervalコンポーネントがマウントされたときに一度だけ呼び出してから呼び出しsetIntervalますsetTime(Date.now()) 1000秒ごとに。最後clearIntervalに、コンポーネントがマウント解除されたときに呼び出します。

コンポーネントはtime、値のtime変更のたびに、コンポーネントでの使用方法に基づいて更新されることに注意してください。それはパットとは何の関係もありませんtime[]のをuseEffect


1
なぜ[]2番目の引数として空の配列()を渡したのuseEffectですか?
Mekeor Melire

効果フックのドキュメントを反応させるのを教えてくれる:「あなたが効果を実行し、一度だけ(マウントとアンマウントに)それをきれいにしたい場合は、2番目の引数として空の配列([])を渡すことができます。」しかしOPは、エフェクトが毎秒実行されることを望んでいます。
Mekeor Melire

配列で[time]を渡した場合に気づいた違いは、間隔ごとにコンポーネントを作成および破棄します。空の配列[]を渡すと、コンポーネントを更新し続け、ページを離れるときのみアンマウントします。しかし、どちらの場合でも、それを機能させるには、key = {time}またはkey = {Date.now()}を追加して実際に再レンダリングする必要がありました。
Teebu

8

コンポーネントのcomponentDidMountライフサイクルメソッドでは、状態を更新する関数を呼び出す間隔を設定できます。

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

4
正解ですが、トップの回答が示唆しているように、メモリリークを回避するために時間をクリアすることを忘れないでください。}
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

それであなたは正しい軌道に乗っていました。内部では、変更をトリガーするようにcomponentDidMount()実装setInterval()することでジョブを完了できますが、コンポーネントの状態を更新する方法は経由setState()であることを覚えておいてくださいcomponentDidMount()。したがって、内部では次のようにすることができます。

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

また、あなたは私が上記で提供Date.now()したcomponentDidMount()実装で機能するを使用しますが、人間が読めない厄介な数字の更新の長いセットを取得しますが、技術的には1970年1月1日からミリ秒で毎秒更新されますが、その学習と実装に加えて、我々は、読み取り時間を人間どのようにこの時間が読めるようにしたいsetInterval、あなたが学びたいnew Date()toLocaleTimeString()、あなたはそれがとても好きで実装します:

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

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

constructor()関数も削除したことに注意してください。必ずしも必要ではありません。私のリファクタリングは、constructor()関数でサイトを初期化することと100%同等です。


0

componentWillReceiveProps()が廃止されたReact V16の変更により、これは、コンポーネントの更新に使用する方法です。以下の例はTypescriptにあり、静的なgetDerivedStateFromPropsメソッドを使用して、Propsが更新されるたびに初期状態と更新された状態を取得することに注意してください。

    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>;
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.