อัปเดตองค์ประกอบการตอบสนองทุกวินาที


115

ฉันเล่นกับ 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;

วิธีใดเป็นวิธีที่ดีที่สุดในการทำให้ส่วนประกอบนี้อัปเดตทุก ๆ วินาทีเพื่อดึงเวลาจากมุมมองของปฏิกิริยาอีกครั้ง

คำตอบ:


153

คุณจำเป็นต้องใช้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

ดูรหัสต้นฉบับได้ที่นี่: 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 ถูกผูกไว้อย่างถูกต้องกับองค์ประกอบนี้
baldrs

16
สำหรับคนงี่เง่าอย่างฉัน: อย่าตั้งชื่อตัวจับเวลาupdaterเพราะมันทำลายรอบการอัปเดต
baldrs

3
ด้วย ES6 ฉันต้องเปลี่ยนหนึ่งบรรทัดเช่น this.interval = setInterval (this.tick.bind (this), 1000);
Kapil

คุณสามารถเพิ่มลิงค์ไปยัง url ที่อ้างอิงได้หรือไม่? ขอบคุณ ~
frederj

ฉันเพิ่มวิธีการจัดรูปแบบและคุณสมบัติ "ตัวสร้าง" เพื่อให้มีประสิทธิภาพมากขึ้น
Mr.Polywhirl

15

@Waisky แนะนำ:

คุณจำเป็นต้องใช้setIntervalเพื่อเริ่มการเปลี่ยนแปลง แต่คุณต้องล้างตัวจับเวลาเมื่อถอนการต่อเชื่อมส่วนประกอบเพื่อป้องกันไม่ให้เกิดข้อผิดพลาดและหน่วยความจำรั่ว:

หากคุณต้องการทำสิ่งเดียวกันโดยใช้ Hooks:

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

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

เกี่ยวกับความคิดเห็น:

[]คุณไม่จำเป็นต้องผ่านการสอบภายในอะไร หากคุณผ่านtimeในวงเล็บหมายความว่าเรียกใช้เอฟเฟกต์ทุกครั้งที่มีค่าของtimeการเปลี่ยนแปลงกล่าวคือเรียกใช้ใหม่setIntervalทุกครั้งtimeการเปลี่ยนแปลงซึ่งไม่ใช่สิ่งที่เรากำลังมองหา เราต้องการเรียกใช้เพียงsetIntervalครั้งเดียวเมื่อส่วนประกอบได้รับการติดตั้งแล้วsetIntervalเรียกsetTime(Date.now())ทุกๆ 1,000 วินาที ในที่สุดเราจะเรียกใช้clearIntervalเมื่อส่วนประกอบถูกยกเลิกการต่อเชื่อม

โปรดทราบว่าส่วนประกอบจะได้รับการอัปเดตตามวิธีที่คุณใช้timeในทุกครั้งที่มีtimeการเปลี่ยนแปลงมูลค่า ที่มีอะไรจะทำอย่างไรกับการวางtimeในของ[]useEffect


1
คุณไม่อาเรย์ว่าง (ทำไม[]) เป็นอาร์กิวเมนต์ที่สองเพื่อuseEffect?
Mekeor Melire

React เอกสารบนตะขอผลที่บอกเราว่า“ถ้าคุณต้องการใช้ผลและทำความสะอาดมันขึ้นเพียงครั้งเดียว (บนภูเขาและยกเลิกการต่อเชื่อม), คุณสามารถส่งผ่านอาร์เรย์ที่ว่างเปล่า ([]) เป็นอาร์กิวเมนต์สอง.” แต่ OP ต้องการให้เอฟเฟกต์ทำงานทุกวินาทีนั่นคือการเกิดซ้ำไม่ใช่แค่ครั้งเดียว
Mekeor Melire

ความแตกต่างที่ฉันสังเกตเห็นหากคุณผ่าน [เวลา] ในอาร์เรย์มันจะสร้างและทำลายส่วนประกอบทุกช่วงเวลา หากคุณส่งอาร์เรย์ว่าง [] มันจะรีเฟรชคอมโพเนนต์ต่อไปและจะยกเลิกการต่อเชื่อมเมื่อคุณออกจากหน้าเท่านั้น แต่ไม่ว่าในกรณีใดเพื่อให้ใช้งานได้ฉันต้องเพิ่ม key = {time} หรือ key = {Date.now ()} เพื่อให้มันแสดงผลจริง
Teebu

8

ในcomponentDidMountวิธีวงจรชีวิตของคอมโพเนนต์คุณสามารถกำหนดช่วงเวลาเพื่อเรียกใช้ฟังก์ชันที่อัปเดตสถานะได้

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

4
ถูกต้อง แต่ตามที่คำตอบด้านบนแนะนำอย่าลืมล้างเวลาเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ: componentWillUnmount () {clearInterval (this.interval); }
Alexander Falk

4
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()ใช้งานที่ฉันเสนอข้างต้น แต่คุณจะได้รับชุดตัวเลขที่น่ารังเกียจซึ่งไม่สามารถอ่านได้โดยมนุษย์ แต่ในทางเทคนิคแล้วเวลาอัปเดตทุกวินาทีในหน่วยมิลลิวินาทีตั้งแต่วันที่ 1 มกราคม 1970 แต่เรา ต้องการที่จะทำให้เวลานี้อ่านวิธีการที่เรามนุษย์เวลาอ่านดังนั้นนอกจากการเรียนรู้และการดำเนินการ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

เนื่องจากมีการเปลี่ยนแปลงใน React V16 ซึ่งมีการเลิกใช้งาน componentWillReceiveProps () นี่คือวิธีการที่ฉันใช้ในการอัปเดตส่วนประกอบ โปรดสังเกตว่าตัวอย่างด้านล่างอยู่ใน typescript และใช้เมธอด getDerivedStateFromProps แบบคงที่เพื่อรับสถานะเริ่มต้นและสถานะที่อัพเดตเมื่อใดก็ตามที่มีการอัพเดตอุปกรณ์ประกอบฉาก

    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.