ReactアプリのsetInterval


100

私はまだReactでかなり新しいですが、ゆっくりと作業を続けており、行き詰まっていることに遭遇しました。

私はReactで「タイマー」コンポーネントを構築しようとしていますが、正直に言うと、これが正しく(または効率的に)行われているかどうかわかりません。以下の私のコードでは、私は、オブジェクトを返すように状態を設定{ currentCount: 10 }し、いじるされているcomponentDidMountcomponentWillUnmountと、render私は唯一の10から9に「カウントダウン」に状態を得ることができます。

2つの部分からなる質問:何が問題になっていますか?そして、(componentDidMount&を使用するよりも)setTimeoutを使用するより効率的な方法がありますcomponentWillUnmount)ますか?

前もって感謝します。

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;

2
bind(this)もう必要ありません。reactはこれだけですぐに動作します。
Derek Pollard、2016年

2
タイマーメソッドはcurrentCountを更新しません
Bryan Chen

1
@デレク、よろしいですか?this.timer.bind(this)this.timerはそれ自体では機能しなかったので、私はこれを追加して動作させました
ワーム

6
@Theworm @Derekは間違っています。React.createClass(非推奨)はメソッドをclass Clock extends Component自動バインドしますが、自動バインドしません。したがって、バインドする必要があるかどうかは、コンポーネントの作成方法によって異なります。
CallMeNorm 2017年

回答:


156

私はあなたのコードに4つの問題を見ます:

  • タイマーメソッドでは、常に現在のカウントを10に設定します
  • あなたはrenderメソッドで状態を更新しようとします
  • setStateメソッドを使用して実際に状態を変更しない
  • 状態にintervalIdを保存していません

それを修正してみましょう:

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

これにより、タイマーが10から-Nに減少します。タイマーが0まで減少するようにしたい場合は、少し変更したバージョンを使用できます。

timer: function() {
   var newCount = this.state.currentCount - 1;
   if(newCount >= 0) { 
       this.setState({ currentCount: newCount });
   } else {
       clearInterval(this.state.intervalId);
   }
},

ありがとうございました。これは非常に理にかなっています。私はまだ初心者であり、状態がどのように機能し、レンダーのようにどの「チャンク」に何が入るのかを把握しようとしています。
ホセ

しかし、実際に間隔を設定するには、componentDidMountとcomponentWillUnmountを使用する必要があるのでしょうか。編集:あなたの最新の編集を見た。:)
ホセ

@Jose componentDidMountはクライアント側イベントをトリガーするのに適切な場所だと思うので、カウントダウンを開始するために使用します。初期化のために他にどのような方法を考えていますか?
dotnetom 2016年

私は特に何も考えていませんでしたが、コンポーネント内で非常に多くの「チャンク」を使用するのは不格好なようでした。私は、Reactでビットとピースがどのように機能するかに慣れているだけだと思います。再びありがとう!
ホセ

4
レンダリングに影響しないため、setInterval値を状態の一部として保存する必要はありません
Gil

32

を使用して更新された10秒のカウントダウン 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;

20

フックを使用して更新された10秒のカウントダウン(クラスを記述せずに状態およびその他のReact機能を使用できるようにする新機能の提案。現在、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'));

React 16.8では、React Hooksは安定したリリースで利用できます。
Greg Herbowicz

2

ありがとう@ dotnetom、@ greg-herbowicz

「this.state is undefined」を返す場合-タイマー関数をバインドします。

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

2

誰かがsetIntervalを実装するためのReact Hookアプローチを探している場合。ダンアブラモフは彼のブログでそれについて話しました。クラスのアプローチを含む主題についてよく読んで欲しい場合は、チェックしてください。基本的に、コードはsetIntervalを宣言型に変更するカスタムフックです。

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

便宜上、CodeSandboxリンクも投稿します。https://codesandbox.io/s/105x531vkq


0

反応クラスで毎秒状態を更新します。my index.jsが現在の時間を返す関数を渡すことに注意してください。

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