初级代码
import React, {useEffect, useRef, useState} from 'react';
function App() {
const [time, setTime] = useState(10)
useEffect(()=>{
// 顺便提一句,这里写成setTimeout也没事
const timer= setInterval(()=>{
if(time>0){
setTime(time - 1);
}
},1000)
console.log("tick", timer);
return ()=>{
clearInterval(timer)
}
})
return (
<div className="App">
<span>{time}</span>
</div>
);
}
export default App;
核心思路: useEffect没有第二个参数,每次渲染都会重新执行方法体,缺陷是生成了多个timer示例
改进: 网上的文章说可以用ref保存time变量。ref的作用是一个独立在渲染循环之外的一个全局变量
import React, {useEffect, useRef, useState} from 'react';
function App() {
let timerRef = useRef<any>(null)
const [time, setTime] = useState(10)
// 增加了这里
useEffect(()=>{
timerRef.current = time
})
useEffect(()=>{
const timer= setInterval(()=>{
if(timerRef.current > 0){
// 这里直接使用引用
setTime(timerRef.current - 1);
}
},1000)
console.log("tick", timer);
return ()=>{
clearInterval(timer)
}
// 这里只在初始化执行
},[])
return (
<div className="App">
<span>{time}</span>
</div>
);
}
export default App;
但年轻的我认为这种方法目前看来是脱裤子放屁,因为从代码看,即使在初始化时候定义一个seInterval就足够了
因此有下面的代码
import React, {useEffect, useRef, useState} from 'react';
function App() {
const [time, setTime] = useState(10)
useEffect(()=>{
const timer= setInterval(()=>{
if(time>0){
setTime(time - 1); // *这里有点问题*
setTime(time=>time - 1);// *改成这样*
}
},1000)
console.log("tick", timer);
return ()=>{
clearInterval(timer)
}
},[])
return (
<div className="App">
<span>{time}</span>
</div>
);
}
export default App;
执行一下发现有点毛病,interval的方法体正常执行,但time的值只变化一次就不再更新,应该是settime后续都没有取到最新的time值稍微做一下改动。
if(time>0){
setTime(time - 1); // *这里有点问题*
setTime(time=>time - 1);// *改成这样*
}
但这样还不够,在if(time)这里的判断中time还是拿不到最新的值,导致定时器没法停驶,所以还是ref的方式更好一点。
一共学到了以下知识:
- setState最好使用函数格式
- useRef的使用
- useEffect不加第二个参数,每次重新渲染都会重新执行