反应钩子:新状态值未反映在 setInterval 回调中
Posted
技术标签:
【中文标题】反应钩子:新状态值未反映在 setInterval 回调中【英文标题】:React hooks: new state value not reflecting in setInterval callback 【发布时间】: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
【参考方案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
【讨论】:
以上是关于反应钩子:新状态值未反映在 setInterval 回调中的主要内容,如果未能解决你的问题,请参考以下文章