如何解决 React Hook 关闭问题?

How To Solve The React Hook Closure Issue?


import React, { useState} from "react";
import ReactDOM from "react-dom";

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

  function handleAlertClick(){
    return (setTimeout(() => {
  alert("You clicked on: " + count);
}, 3000))
  }


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

我只想知道这是否按照我认为的方式工作,或者是否有更好的解释!

只要调用 setState 方法,状态就会得到一个新的引用。这意味着原始状态没有新值,但我们创建了一个具有新值的新状态。当我们点击第二个按钮时,事件处理函数捕获原始状态的引用。即使我们多次点击第一个按钮,当显示警报时,它也会显示事件处理程序捕获其引用的状态值。

这是正确的吗?

alert 显示 count 的过时值的原因是因为传递给 setTimeout 的回调引用了 count 捕获的过时值 关闭。这通常被称为 stale-closure.

在初始渲染中,作为回调传递给 setTimeout 的匿名函数捕获 count 的值作为 0,并且当按钮 show alert 被点击时回调已排队,但计数值已过时。

在上述情况下,在警报消息中显示计数的更新值并修复陈旧关闭问题的最简单解决方案是使用 ref.

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

  const latestValue = useRef(count);

  const handleAlertClick = () => {
    setTimeout(() => {
      alert(`count is: ${latestValue.current}`);
    }, 3000);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button
        onClick={() => {
          setCount(prev => {
            latestValue.current = prev + 1;
            return prev + 1;
          });
        }}
      >
        Click me
      </button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}

codesandbox

中的工作演示

Hooks 严重依赖闭包来工作,因此您很可能会遇到有关 stale-closures 的问题。这是一个很好的 article 关于陈旧闭包如何在使用 react-hooks 时产生问题,并演示了如何在某些情况下解决一些问题。

https://dmitripavlutin.com/react-hooks-stale-closures/#32-usestate

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

  const handleAlertClick = () => {
    setTimeout(() => {
      alert(`count is: ${count}`);
    }, 3000);
  };

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