首页 > 解决方案 > 使用父状态反应计时器

问题描述

我有一个计时器组件,它是 A 的子组件。我将播放和暂停作为父级的状态值并将其传递给子级。

我想在 play 为真时为子组件启动一个 setTimeout,子组件将从钩子状态获得总持续时间。

我正在使用钩子将总持续时间存储在子项中,并在 useEffect 中有一个 setTimeout 函数,如果 play 的值为 true,然后通过将初始值减 1 来覆盖钩子状态值。

https://stackblitz.com/edit/react-nlxodz有 2 个计时器,想知道我在 ShowTimer 中所做的是否是一个好方法,每次状态更新都会调用 useEffect 的清理功能,而 Timer 组件是与 ShowTimer 不同步,即使它们都以 10 秒开始。

标签: javascriptreactjs

解决方案


我会说不,这不是设置计时器的好方法,尽管您的showTimer方法很接近。

两者setTimeout()setInterval()都不准确,不能被信任。更好的方法是使用Date.now()方法:

const App = () => {
  const [isStart, setIsStart] = React.useState(false)
  const [duration, setDuration] = React.useState(10)
        
  const handleStart = () => setIsStart(!isStart)
  
  React.useEffect(() => {
    let interval = null
    if (!isStart || duration === 0) return
    else {
      const now = Date.now() + duration * 1000
      interval = setInterval(() => {
        const secondLeft = Math.round((now - Date.now()) / 1000)
        setDuration(secondLeft)
        if (secondLeft === 0) {
          setIsStart(false)
          clearInterval(interval)
        }
      }, 1000)
    }
    console.log("Effect render, Called because duration is changes")

    return () => {
      clearInterval(interval)
      console.log("Cleanup Effect")
    }
  }, [duration, isStart])
  
  return (
    <div className="App">
      <button onClick={handleStart} style={{ marginRight: 10 }}>
        {isStart && duration > 0 ? "Pause" : "Start"}
      </button>
      <span>{duration}</span>
    </div>
  )
}

ReactDOM.render( <App /> , document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>


推荐阅读