如何解决 React Hook 关闭问题?

Posted

技术标签:

【中文标题】如何解决 React Hook 关闭问题?【英文标题】:How To Solve The React Hook Closure Issue? 【发布时间】:2020-10-29 13:22:47 【问题描述】:

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 方法时,状态都会获得一个新的引用。这意味着原始状态没有新值,而是我们创建具有新值的新状态。当我们单击第二个按钮时,事件处理函数会捕获原始状态的引用。即使我们多次单击第一个按钮,当显示警报时,它也会显示事件处理程序捕获其引用的状态值。

这是正确的吗?

【问题讨论】:

你遇到了什么问题? 我只是问我所了解的是否正确!每当调用 setState 方法时,状态都会获得一个新的引用。这意味着原始状态没有新值,而是我们创建具有新值的新状态。当我们单击第二个按钮时,事件处理函数会捕获原始状态的引用。即使我们多次单击第一个按钮,当显示警报时,它也会显示事件处理程序捕获其引用的状态值 【参考方案1】:

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 时过时的闭包如何产生问题,并演示了如何在某些情况下解决一些问题。

【讨论】:

我不认为这是关于捕获count的值,而是关于捕获count变量的引用,你同意我的观点吗?请阅读我在问题代码下方所说的内容! 与状态无关。该行为完全与闭包的工作方式以及闭包如何捕获过时的变量有关。该问题可以很容易地在函数中重现。这不仅限于反应。请参阅article 中提供的第一个示例 这不仅仅是捕获过时的变量,如果是这种情况,为什么值变量窃取的值更新了?这都是关于引用的问题是,每当我们调用 inc() 函数时,消息变量都会使用内存中的新地址进行初始化,这意味着闭包会捕获第一条消息的引用,这就是为什么当我们调用 log 函数时它会返回值1. 如果我们尝试用 value 变量初始化顶部的 message 变量,这将解决问题,因为我们在任何时候调用 inc 函数时都不需要初始化 message 变量! 好吧,当我说引用时,我刚刚意识到我们的不同之处,我的意思是闭包捕获变量的引用,以便在它想要使用该引用的值时返回它!我们处理的是闭包不捕获值而是捕获变量(在我的情况下我的意思是引用)! 请注意,useRef 创建的对象将在组件的整个生命周期内持续存在,并且 React 将为您提供每次渲染时相同的 ref 对象。这是因为通过引用内存中的同一个对象,任何回调,无论其定义或执行时的范围如何,都指向同一个对象,从而消除了过时的引用。

以上是关于如何解决 React Hook 关闭问题?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决“React Hook useEffect 缺少依赖项。包括它或删除依赖项数组”问题?

如何使用 react-hook-form 有条件地禁用提交按钮?

如何解决游戏中出现“fail to hook gettickcount”的问题?

react-hook-form 控制器问题

react-hook-form yup 解析器,reactStrap 解决子组件错误的问题

错误:无法在回调中调用 React Hook “useLogout”。阿波罗