React挂钩:新状态值未反映在setInterval回调中

时间:2019-04-07 20:17:10

标签: javascript reactjs react-hooks

我有一个函数react组件,该组件的计数器从10000开始到0。

我在组件安装期间使用useEffect钩子设置了setInterval回调。然后,回调将更新计数器状态。

但是我不知道为什么,count的值永远不会减少。每次回调运行count都是10000。

(我正在使用react&react-dom版本16.8.3

功能组件如下:

import React, { useState, useEffect, useRef } from 'react'

const Counter = () => {
  const timerID = useRef()
  let [count, setCount] = useState(10000)

  useEffect(() => {
    timerID.current = setInterval(() => {
      //count here is always 10000
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 1)
  }, [])

  return <h1 className="counter">{count}</h1>
}

export default Counter

以下是指向codeandbox的链接:link

3 个答案:

答案 0 :(得分:3)

您需要注意count中的更改,并清理useEffect()

useEffect(() => {
    timerID.current = setInterval(() => {
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 100)

    return () => clearInterval(timerID.current);
  }, [count])

如@Pavel所述,Dan Abramov解释了为什么here

答案 1 :(得分:1)

您正在按照您所说的那样在安装组件时声明效果功能。因此,在该范围内,时间值存储内部计数等于10000。这意味着每个时间间隔函数执行都从闭包(10000)中获取计数值。正确地做到这一点实际上很难。丹(Dan)为此写了整本blog post

答案 2 :(得分:1)

有2个选项:

1)在依赖项中包含count

这并不理想,因为这意味着将在setInterval的每次更改中创建一个新的count,因此您需要在每次渲染时都将其清除,例如:

  useEffect(() => {
    timerID.current = setInterval(() => {
      //count here is always 10000
      if (count - 1 < 0) {
        setCount(0)
      } else {
        setCount(count - 1)
      }
    }, 1)
    return () => clearInterval(timerID.current) // Added this line
  }, [count]) // Added count here

2)在count回调函数中添加setInterval

这是间隔的最佳方法,因为它避免了始终设置新间隔的方法。

 useEffect(() => {
    timerID.current = setInterval(() => {
      // count is used inside the setCount callback and has latest value
      setCount(count => {
        if (count - 1 < 0) { // Logic moved inside the function, so no dependencies
          if (timerID.current) clearInterval(timerID.current)
          return 0
        }
        return count - 1
      })
    }, 1)
    return () => {
      if (timerID.current) clearInterval(timerID.current) // Makes sure that the interval is cleared on change or unmount
    }
  }, [])

这里是sandbox link