关于react倒计时的一些总结

39 阅读2分钟

初级代码

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不加第二个参数,每次重新渲染都会重新执行