useState 钩子 - 传入回调与状态值

useState hook - passing in callback vs state value

在这个非常简单的点击计数器示例中:

const App = () => {
  const [count, setCount] = useState(0)

  const handleClick = () => {
    setCount(count + 1)
  }
  return <button onClick={handleClick}>{count}</button>
}

我对流量的理解是:

  1. 组件安装 count=0
  2. 单击按钮时,新的 count 状态设置为增量 1。
  3. 这会触发重新渲染,所以现在我有 count=1,然后重复。

但是,用同样的理解,为什么这里不行呢?

const App = () => {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1)
    }, 500)

    return () => {
      clearInterval(timer)
    }
  }, [])

  console.log(count) // this stops at 1, so the timer stops triggering??
  return <h1>{count}</h1> // get stuck at 1
}

上述代码的结果是 count 卡在值 1。(奇怪的是 console.log 也停止了)。

我认为每次触发 setInterval 计时器时,计数增量都会导致重新渲染 new count 值,因此它只会永远增加 1 ?

这里的解决方法是简单地传入一个函数参数来访问 prevState:

const timer = setInterval(() => {
      setCount(oldCount => oldCount + 1)
    }, 500)

但为什么第一种方法行不通? 希望有人能给我指出一篇好的文章或文档,我尝试四处搜索但找不到任何解释。

第一种方法行不通,因为这个

setCount(count + 1)

您正在创建回调的那个特定时间的 count 值的副本。这意味着每 500 毫秒你将 re-execute 这一行

setCount(0 + 1)

这不会导致 re-render,因为 React 足够智能,可以理解您将相同的值传递给 setCount 函数,因此 re-render 不是必需的。

然而,通过将回调传递给 setCount:

setCount(oldCount => oldCount + 1)

你是说你想要状态 count 的当前值,所以每次该函数的参数都会不同,因此会导致 re-render.

您可以在此处找到有关此主题的文档:https://reactjs.org/docs/hooks-reference.html#functional-updates