父组件使用 CountDown组件
import { CountDown } from '@/components'// 不写默认值是 60秒const lastTime = 60function Page() {const onFinish = () => {console.log('end')}return (<div><CountDown onEnd={onFinish} value={lastTime} /></div>)}export default Page
CountDown
import { useState, useEffect } from 'react';interface IProps {time: number;onEnd: () => void;}const CountDown = (props: IProps) => {const { time, onEnd } = props;const [count, setCount] = useState(time || 60);useEffect(() => {const timer = setInterval(() => {setCount((count) => {if (count === 0) {clearInterval(timer);onEnd && onEnd();return count;}return count - 1;});}, 1000);return () => {clearInterval(timer);};}, [time, onEnd]);return <div>{count}</div>;};export default CountDown;
RAF.setTimeout
import React, { PureComponent } from 'react'import PropTypes from 'prop-types'import RAF from '@utils/requestAnimationFrame'/*** @description 60秒倒计时* react组件生命周期:* constructor -> componentWillMount -> render -> componentDidMount*/class CountDown extends PureComponent {timer = nullinterval = 1000state = {second: this.props.value}static propTypes = {value: PropTypes.number,onChange: PropTypes.func.isRequired}static defaultProps = {value: 60,onChange: () => { }}componentDidMount() {if (this.timer) RAF.clearTimeout(this.timer)this.tick()}componentWillUnmount() {if (this.timer) RAF.clearTimeout(this.timer)}tick = () => {let { second } = this.stateconst { value, onChange } = this.propsthis.timer = RAF.setTimeout(() => {if (second <= 1) {onChange()this.setState({ second: value }, () => this.tick())return}second -= 1this.setState({ second }, () => this.tick())}, 1000)}render() {const { second } = this.stateconst text = `${second}`.padStart(2, '0')return <span>{text}</span>}}export default CountDown
requestAnimationFrame
requestAnimationFrame实现 setTimeout,setInterval
/*** @description requestAnimationFrame 实现 setInterval,解决内存溢出** 页面实时的请求接口,控制组件的位置。当大量组件使用了计时器,会造成网页内存溢出*/const [initCallback, initInterval] = [() => { }, 1000]const requestAnimationFrame = window.requestAnimationFrameconst RAF = {intervalTimer: null,timeoutTimer: null,// 实现 setTimeout 倒计时setTimeout(callback = initCallback, interval = initInterval) {const startTime = Date.now()const loop = () => {this.timeoutTimer = requestAnimationFrame(loop)const endTime = Date.now()if (endTime - startTime >= interval) {// 一定要先清除,后调用 callback,不然死循环this.clearTimeout()callback()}}this.timeoutTimer = requestAnimationFrame(loop)return this.timeoutTimer},// 想重复不断的执行 requestAnimationFrame 实现 setIntervalsetInterval(callback = initCallback, interval = initInterval) {let startTime = Date.now()const loop = () => {this.intervalTimer = requestAnimationFrame(loop)const endTime = Date.now()if (endTime - startTime >= interval) {startTime = endTimecallback()}}this.intervalTimer = requestAnimationFrame(loop)return this.intervalTimer},// 不要使用箭头函数,this指向 undefinedclearTimeout() { cancelAnimationFrame(this.timeoutTimer) },clearInterval() { cancelAnimationFrame(this.intervalTimer) },}export default RAF
class封装 RAF
/*** @description requestAnimationFrame 实现 setInterval,解决内存溢出** 页面实时的请求接口,控制组件的位置。当大量组件使用了计时器,会造成网页内存溢出*/const [initCallback, initInterval] = [() => { }, 1000]const requestAnimationFrame = window.requestAnimationFrameclass RAF {constructor() {this.intervalTimer = nullthis.timeoutTimer = nullthis.currentTime = null}setInterval(callback = initCallback, interval = initInterval) {this.currentTime = Date.now()const loop = () => {this.intervalTimer = requestAnimationFrame(loop)const endTime = Date.now()if (endTime - this.currentTime >= interval) {this.currentTime = endTimecallback()}}this.intervalTimer = requestAnimationFrame(loop)return this.intervalTimer}setTimeout(callback = initCallback, interval = initInterval) {this.currentTime = Date.now()const loop = () => {this.timeoutTimer = requestAnimationFrame(loop)const endTime = Date.now()if (endTime - this.currentTime >= interval) {// 一定要先清除,后调用 callback,不然死循环this.clearTimeout()callback()}}this.timeoutTimer = requestAnimationFrame(loop)return this.timeoutTimer}clearTimeout() {return cancelAnimationFrame(this.timeoutTimer)}clearInterval() {return cancelAnimationFrame(this.intervalTimer)}}export default RAF
setTimeout
setTimeout实现倒计时
import React, { PureComponent } from 'react'import PropTypes from 'prop-types'/*** @description 60秒倒计时* react组件生命周期:* constructor -> componentWillMount -> render -> componentDidMount*/class CountDown extends PureComponent {timer = nullinterval = 1000state = {second: this.props.value}static propTypes = {value: PropTypes.number,onChange: PropTypes.func.isRequired}static defaultProps = {value: 60,onChange: () => {}}componentDidMount() {if (this.timer) clearTimeout(this.timer)this.tick()}componentWillUnmount() {if (this.timer) clearTimeout(this.timer)}tick = () => {let { second } = this.stateconst { value, onChange } = this.propsthis.timer = setTimeout(() => {if (second <= 0) {onChange()this.setState({ second: value }, () => this.tick())return}second -= 1this.setState({ second }, () => this.tick())}, this.interval)}render() {const { second } = this.statereturn <span>{second}</span>}}export default CountDown
