setInterval у додатку React


101

Я все ще досить новий в React, але я повільно перемелюся і стикаюся з чимось, на чому я застряг.

Я намагаюся створити "таймер" компонент в React, і якщо чесно, я не знаю, чи правильно це я роблю (або ефективно). У моєму коді нижче, я встановити стан , щоб повернути об'єкт { currentCount: 10 }і грав з componentDidMount, componentWillUnmountі renderя можу отримати тільки стан на « зворотний відлік» від 10 до 9.

Питання з двох частин: у чому я помиляюся? І чи існує більш ефективний спосіб використання setTimeout (а не використання componentDidMount& 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)більше не потрібно, реагуйте, робить це самостійно зараз.
Дерек Поллард,

2
ваш метод таймера не оновлює поточну кількість
Брайан Чен

1
@Derek ти впевнений? Я щойно this.timer.bind(this)запустив свою роботу, додавши , що цей таймер сам по собі не спрацював
Черв'як

6
@Theworm @Derek помиляється. React.createClass (застарілий) class Clock extends Componentавтоматично прив'язує методи, але не прив'язує автоматично. Тож від того, як ви створюєте свої компоненти, залежить, чи потрібно прив’язувати.
CallMeNorm

Відповіді:


157

Я бачу 4 проблеми з вашим кодом:

  • У вашому методі таймера ви завжди встановлюєте поточну кількість 10
  • Ви намагаєтесь оновити стан у методі візуалізації
  • Ви не використовуєте 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, щоб насправді встановити інтервал? EDIT: Щойно побачив ваше останнє редагування. :)
Хосе

@Jose, я думаю, що componentDidMountце правильне місце для ініціювання подій на стороні клієнта, тому я б використав це для ініціювання зворотного відліку. Який ще метод ви думаєте для ініціалізації?
дотнетом

Я нічого іншого особливо не мав на увазі, але, здавалося, незграбно використовувати стільки "шматочків" всередині компонента. Я припускаю, що я просто звикаю до того, як шматочки та шматочки працюють у React. Ще раз дякую!
Хосе

4
Немає реальної потреби зберігати значення setInterval як частину стану, оскільки це не впливає на візуалізацію
Жил

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 доступні у стабільному випуску.
Грег Гербович

2

Дякую @dotnetom, @ greg-herbowicz

Якщо він повертає "this.state не визначено" - функція таймера прив'язки:

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

2

Якщо хтось шукає підхід React Hook до реалізації setInterval. Про це Ден Абрамов розповів у своєму блозі . Перевірте, чи хочете ви добре прочитати цю тему, включаючи підхід до класу. В основному код - це спеціальний хук, який робить 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.