setInterval ในแอป React


107

ฉันยังค่อนข้างใหม่ที่ React แต่ฉันค่อยๆบดไปเรื่อย ๆ และฉันเจอบางอย่างที่ฉันติดขัด

ฉันกำลังพยายามสร้างองค์ประกอบ "ตัวจับเวลา" ใน React และพูดตามตรงว่าฉันไม่รู้ว่าฉันทำถูกต้อง (หรือมีประสิทธิภาพ) ในรหัสของฉันด้านล่างผมตั้งรัฐที่จะกลับวัตถุ{ currentCount: 10 }และได้รับ toying กับ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)ไม่จำเป็นอีกต่อไปทำปฏิกิริยาด้วยตัวเองในตอนนี้
Derek Pollard

2
วิธีการจับเวลาของคุณไม่อัปเดต currentCount
Bryan Chen

1
@ เดเร็คแน่ใจเหรอ ฉันเพิ่งทำงานของฉันโดยเพิ่มthis.timer.bind(this)เป็นสิ่งนี้ตัวจับเวลาด้วยตัวเองไม่ได้ผล
หนอน

6
@Theworm @Derek ผิดประเภท. React.createClass (ซึ่งเลิกใช้แล้ว) วิธีการค้นหาclass Clock extends Componentอัตโนมัติแต่ไม่ผูกอัตโนมัติ ดังนั้นจึงขึ้นอยู่กับวิธีการสร้างส่วนประกอบของคุณว่าคุณต้องการผูกไหม
CallMeNorm

คำตอบ:


163

ฉันพบปัญหา 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);
   }
},

ขอบคุณ. สิ่งนี้เข้าท่ามาก ฉันยังคงเป็นมือใหม่อยู่มากและฉันก็พยายามที่จะเข้าใจวิธีการทำงานของสถานะและสิ่งที่เป็น "ชิ้นส่วน" เช่นการแสดงผล
Jose

ฉันสงสัยว่าจำเป็นต้องใช้ componentDidMount และ componentWillUnmount เพื่อกำหนดช่วงเวลาจริงหรือไม่ แก้ไข: เพิ่งเห็นการแก้ไขล่าสุดของคุณ :)
Jose

@Jose ฉันคิดว่าcomponentDidMountเป็นสถานที่ที่เหมาะสมในการทริกเกอร์เหตุการณ์ฝั่งไคลเอ็นต์ดังนั้นฉันจะใช้มันเพื่อเริ่มการนับถอยหลัง วิธีการอื่นที่คุณคิดเกี่ยวกับการเริ่มต้น?
dotnetom

4
ไม่จำเป็นต้องเก็บค่า setInterval ไว้เป็นส่วนหนึ่งของสถานะเพราะไม่มีผลต่อการเรนเดอร์
Gil

1
ใน ES6 อย่าลืมผูกคลาสเพื่อเข้าถึงสิ่งนี้: this.timer.bind(this)และtimer() { ... }
Vincent Decaux

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 วินาทีโดยใช้Hooks (ข้อเสนอคุณลักษณะใหม่ที่ให้คุณใช้สถานะและคุณสมบัติการตอบสนองอื่น ๆ โดยไม่ต้องเขียนคลาสขณะนี้อยู่ใน 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

หมายเหตุ: ระวัง! นี่ไม่ใช่รหัสที่ถูกต้อง!
likern

4

หากใครกำลังมองหาแนวทาง React Hook ในการนำ setInterval ไปใช้งาน แดน Abramov พูดคุยเกี่ยวกับเรื่องของเขาบล็อก ตรวจสอบว่าคุณต้องการอ่านเกี่ยวกับเรื่องนี้หรือไม่รวมถึงแนวทางการเรียน โดยทั่วไปโค้ดคือ Hook แบบกำหนดเองที่เปลี่ยน setInterval เป็น declarative

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


2

ขอบคุณ @dotnetom, @ greg-herbowicz

หากส่งคืน "this.state is undefined" - ฟังก์ชันตัวจับเวลาการผูก:

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

0

การอัปเดตสถานะทุกวินาทีในคลาสการตอบสนอง สังเกตว่า 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;

0

จัดการ setInterval ด้วย React Hooks:

  const [seconds, setSeconds] = useState(0)

  const interval = useRef(null)

  useEffect(() => { if (seconds === 60) stopCounter() }, [seconds])

  const startCounter = () => interval.current = setInterval(() => {
    setSeconds(prevState => prevState + 1)
  }, 1000)

  const stopCounter = () => clearInterval(interval.current)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.