setTimeout 和 Closure 这里有什么问题?

jak*_*ndo 3 javascript asynchronous settimeout reactjs

首先,我无法找出这段代码中的问题。但这个问题可能在这里有一席之地。

据我所知,问题可能是单击按钮后计数器值没有更新。尽管在 2.5 秒的延迟期间我单击并增加了计数器的值,但警报会在单击按钮时显示该值。

我是对的吗?如果是的话,这里应该修复或添加什么?

import React, { useState } from 'react'

function Root() {
  const [count, setCount] = useState(0)

  function handleAlertClick() {
    setTimeout(() => {
       alert(`You clicked ${count} times`)
      }, 2500)
  }

  return (
    <Container>
      <Column>
        <h4>Closures</h4>
        <p>You clicked {count} times</p>
        <button type="button" onClick={() => setCount(counter => counter + 1)}>
          Click me
        </button>
        <button type="button" onClick={handleAlertClick}>
          Show alert
        </button>
      </Column>
    </Container>
  )
}

export default Root
Run Code Online (Sandbox Code Playgroud)

You*_*saf 5

问题

当 被setTimeout调用时,其回调函数将关闭count组件当前渲染中的当前值Root

在计时器到期之前,如果更新count,会导致组件重新渲染,但回调函数仍会看到调用时有效setTimeout的值。这是由代码中的闭包引起的问题的要点。setTimeout

组件的每个渲染器Root都有自己的状态、道具、组件内部定义的本地函数;简而言之,组件的每个渲染都与其之前的渲染分开。

状态在组件的特定渲染内是恒定的;组件在重新渲染之前无法看到更新后的状态。前一个渲染中设置的任何计时器都会看到它关闭的值;它看不到更新的状态。

解决方案

可以使用useRef钩子来解决由于关闭而导致的问题。

Root您可以在每次重新渲染组件时更新引用。这允许我们保存countref 中的最新值。

一旦有了 ref,就不要传递countalert,而是传递 ref。这可确保警报始终显示 的最新值count

function Root() {
  const [count, setCount] = React.useState(0)
  const countRef = React.useRef(count);

  // assign the latest value of "count" to "countRef"
  countRef.current = count;
  
  function handleAlertClick() {
    setTimeout(() => {
       alert(`You clicked ${countRef.current} times`)
      }, 2500)
  }

  return (
      <div>
        <h4>Closures</h4>
        <p>You clicked {count} times</p>
        <button type="button" onClick={() => setCount(counter => counter + 1)}>
          Click me
        </button>
        <button type="button" onClick={handleAlertClick}>
          Show alert
        </button>
      </div>
  )
}

ReactDOM.render(<Root/>, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)