【问题标题】:React hooks: new state value not reflecting in setInterval callback反应钩子:新状态值未反映在 setInterval 回调中
【发布时间】:2019-08-29 00:06:12
【问题描述】:

我有一个函数 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

【问题讨论】:

  • setState() 是异步的...
  • 不要认为这很重要。
  • setCount(--count ) 有效。可能是最好的方法,也可能不是?这是一个关闭问题
  • 将计数设置为依赖项并使用 setTimeout 将解决您的痛苦:)。因为下次渲染时会在count有新值时再次调用setTimeout

标签: javascript reactjs react-hooks


【解决方案1】:

你需要注意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

【讨论】:

  • 嗨@Colin,每次更新计数时不会设置新的间隔吗?我不希望这种情况发生。 setInterval() 应该在挂载期间只设置一次。
  • 会的。如果您查看博客文章,有一个建议的 useInterval 钩子可以做类似但更复杂的事情。 TLDR;您需要设置并清除每次重新渲染的时间间隔,但这不应该是一项昂贵的操作。
  • 知道了!!想知道在 google 上搜索时我怎么找不到该博客文章...>
【解决方案2】:

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

【讨论】:

  • 知道了!!想知道在谷歌上搜索时我怎么找不到该博客文章...>
【解决方案3】:

有两种选择:

1) 在依赖项中包含count

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

  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) 在setInterval回调函数中添加count

这是最好的间隔方法,因为它避免了一直设置新的间隔。

 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多