定时器
设置计时器
componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行,所以,最好在这里设置计时器:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
接下来把计时器的 ID 保存在 this 之中(this.timerID)。
尽管 this.props 和 this.state 是 React 本身设置的,且都拥有特殊的含义,但是其实你可以向 class 中随意添加不参与数据流(比如计时器 ID)的额外字段。
清除计时器
在 componentWillUnmount() 生命周期方法中清除计时器:
componentWillUnmount() {
clearInterval(this.timerID);
}
DEMO:时钟每秒都会刷新
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
export default Clock
useEffect 副作用清除定时器
const [count, addCount] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
// 创建计时器
addCount((val) => val + 1)
}, 1000)
return () => {
// 返回清除函数
clearInterval(interval) // 清除计时器
}
}, [count])
为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除。
当 count 改变后才会重新创建计时器,也就是说,useEffect 会根据我们传入的第二参数,来决定本次更新是否执行。
DEMO (类组件)
有一个简单的计时器,默认以 1000ms 的时间间隔计数,点击加按钮后,计时时间间隔增加,计时器计数变慢。
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
timer: 1000,
}
}
componentDidMount() {
this.interval = setInterval(() => {
// 创建定时器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentDidUpdate() {
clearInterval(this.interval) // 先清除上一个定时器
this.interval = setInterval(() => {
// 创建以 更新后的timer 计时间隔定时器
this.setState({
count: this.state.count + 1,
})
}, this.state.timer)
}
componentWillUnmount() {
clearInterval(this.interval) // 卸载时清除定时器
}
render() {
return (
<div>
<p>当前count值为:{this.state.count}</p>
<p>当前计数时间间隔为:{this.state.timer}ms</p>
<button onClick={() => this.setState({ timer: this.state.timer + 1000 })}>Click me</button>
</div>
)
}
}
DEMO (使用 Effect Hook 实现)
import React, { useState, useEffect } from 'react'
function Example() {
const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)
useEffect(() => {
const interval = setInterval(() => {
addCount((val) => val + 1)
}, timer)
return () => {
clearInterval(interval)
}
}, [count])
return (
<div>
<p>当前count的值为:{count}</p>
<p>当前计数时间间隔为: {timer}ms</p>
<button onClick={() => addTimer((val) => val + 1000)}>Click me</button>
</div>
)
}
对比
useEffect Hook 实际上为我们带来了,在函数组件中也能处理副作用的功能,他好比componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
直接声明变量
使用计时器或定时器的需求,那么我们在清楚定时器时常常需要一个变量,有时候我们会这样写
import React, { useEffect } from 'react'
import { Button } from 'antd'
const App = () => {
let timer
useEffect(() => {
timer =setInterval(() => {
console.log('触发了')
}, 1000)
},[])
const clearTimer = () => {
clearInterval(timer)
}
return (
<>
<Button onClick={clearTimer}>停止计时</Button>
<div>这是一个纯函数组件</div>
</>)
}
export default App
我们在纯函数的全局作用域内定义了一个变量,用来存放定时任务,通常情况下我也是这么做的,但是react hooks 中给我们提供了useRef这样的hooks,使用他将让我们的代码变得而更加优雅
useRef 存放变量
import React, { useEffect, useRef } from 'react'
import { Button } from 'antd'
const App = () => {
const ref = useRef()
useEffect(() => {
const timer =setInterval(() => {
console.log('触发了')
}, 1000)
ref.current = timer
},[])
const clearTimer = () => {
clearInterval(ref.current)
}
return (
<>
<Button onClick={clearTimer}>停止计时</Button>
<div>这是一个纯函数组件</div>
</>)
}
export default App
在useRef中,他变成了一个可以存放任意值的的“容易”,并且不会随着组件的更新而变化,这样我们在存放一个“固定的变量”的时候,就显得很有用。