”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > React:陈旧的关闭

React:陈旧的关闭

发布于2024-09-02
浏览:595

在这篇文章中,我将展示如何在 useState hook React 应用程序中创建闭包。

我不会解释什么是闭包,因为关于这个主题的资源有很多,我不想重复。我建议阅读 @imranabdulmalik 的这篇文章。

简而言之,闭包是(来自Mozilla):

...捆绑在一起(封闭)的函数及其周围状态(词法环境)的引用的组合。换句话说,闭包使您可以从内部函数访问外部函数的作用域。在 JavaScript 中,每次创建函数时都会创建闭包,在函数创建时.

以防万一您不熟悉术语词汇环境,您可以阅读 @soumyadey 的这篇文章或这篇文章。

问题

在 React 应用程序中,您可能会意外创建属于使用 useState 挂钩创建的组件状态的变量的闭包。当发生这种情况时,您将面临陈旧闭包问题,也就是说,当您引用状态的旧值时,它同时发生了变化,因此它不再相关。

POC

我创建了一个 Demo React 应用程序,其主要目标是增加一个计数器(属于状态),该计数器可以在 setTimeout 方法的回调中的闭包中关闭。

简而言之,这个应用程序可以:

  • 显示计数器的值
  • 计数器加 1
  • 启动计时器,在五秒后将计数器加 1。
  • 计数器加 10

下图中,显示了应用程序的初始 UI 状态,计数器为零。

React: stale closure

我们将分三步模拟计数器的关闭

  1. 计数器加 1

React: stale closure

  1. 启动计时器,五秒后加 1

React: stale closure

  • 超时触发前增加 10 React: stale closure

5秒后,计数器的值为2。

React: stale closure

计数器的期望值应该是12,但我们得到2

发生这种情况的原因是因为我们在传递给setTimeout的回调中创建了计数器的闭包,当触发超时时,我们从其开始设置计数器旧值(即 1)。

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);

遵循应用程序组件的完整代码。

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    解决方案

    该解决方案基于 useRef 钩子的使用,该钩子允许您引用渲染不需要的值。

    所以我们在App组件中添加:

    const currentCounter = useRef(counter)
    

    然后我们修改setTimeout的回调,如下所示:

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current   1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    

    我们的回调需要读取计数器值,因为我们在增加它之前记录了当前值。

    如果您不需要读取值,只需使用功能符号更新计数器即可避免计数器关闭。

    seCounter(c => c   1)
    

    资源

    • Dmitri Pavlutin 使用 React Hooks 时要注意过时的闭包
    • Imran Abdulmalik 掌握 JavaScript 中的闭包:综合指南
    • JavaScript 中的 Keyur Paralkar 词法范围 – 初学者指南
    • React 中的 Souvik Paul Stale 闭包
    • Soumya Dey 理解 JavaScript 中的词法范围和闭包
    • Subash Mahapatra stackoverflow
    版本声明 本文转载于:https://dev.to/animusna/react-stale-closure-81a?1如有侵犯,请联系[email protected]删除
    最新教程 更多>
    • 定制Seaborn图表尺寸,完美打印输出
      定制Seaborn图表尺寸,完美打印输出
      在海洋绘图中自定义图形大小用于打印 1。使用seaborn set_theme方法: RC参数允许您使用字典以英寸为单位指定所需的图形大小。利用matplotlib rcparams: = 11.7,8.27 此方法允许直接操纵matplotlib的配置来设置图形大小。 附加说明:探索seabo...
      编程 发布于2025-04-12
    • 如何使用PHP将斑点(图像)正确插入MySQL?
      如何使用PHP将斑点(图像)正确插入MySQL?
      essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call ...
      编程 发布于2025-04-12
    • 如何从Google API中检索最新的jQuery库?
      如何从Google API中检索最新的jQuery库?
      从Google APIS 问题中提供的jQuery URL是版本1.2.6。对于检索最新版本,以前有一种使用特定版本编号的替代方法,它是使用以下语法:获取最新版本:未压缩)While these legacy URLs still remain in use, it is recommended ...
      编程 发布于2025-04-12
    • 如何在Java中执行命令提示命令,包括目录更改,包括目录更改?
      如何在Java中执行命令提示命令,包括目录更改,包括目录更改?
      在java 通过Java通过Java运行命令命令可能很具有挑战性。尽管您可能会找到打开命令提示符的代码段,但他们通常缺乏更改目录并执行其他命令的能力。 solution:使用Java使用Java,使用processBuilder。这种方法允许您:启动一个过程,然后将其标准错误重定向到其标准输出。...
      编程 发布于2025-04-12
    • 如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
      如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
      如何在“ dd/mm/yyyy hh:mm:mm:ss.ss”格式“ gormat 解决方案: args)抛出异常{ 日历cal = calendar.getInstance(); SimpleDateFormat SDF =新的SimpleDateFormat(“...
      编程 发布于2025-04-12
    • 如何克服PHP的功能重新定义限制?
      如何克服PHP的功能重新定义限制?
      克服PHP的函数重新定义限制 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_rename() runkit_function_redefine() //重新定义'this'以返回“新和改...
      编程 发布于2025-04-12
    • 如何简化PHP中的JSON解析以获取多维阵列?
      如何简化PHP中的JSON解析以获取多维阵列?
      php 试图在PHP中解析JSON数据的JSON可能具有挑战性,尤其是在处理多维数组时。要简化过程,建议将JSON作为数组而不是对象解析。执行此操作,将JSON_DECODE函数与第二个参数设置为true:[&&&&& && &&&&& json = JSON = JSON_DECODE($ j...
      编程 发布于2025-04-12
    • 如何使用Python有效地以相反顺序读取大型文件?
      如何使用Python有效地以相反顺序读取大型文件?
      在python 中,如果您使用一个大文件,并且需要从最后一行读取其内容,则在第一行到第一行,Python的内置功能可能不合适。这是解决此任务的有效解决方案:反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] ...
      编程 发布于2025-04-12
    • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
      如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
      为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
      编程 发布于2025-04-12
    • 使用JavaScript和jQuery处理HTML时如何解决未定义元素问题
      使用JavaScript和jQuery处理HTML时如何解决未定义元素问题
      尝试使用javascript(JS)和JQUERES的录制时,请访问html元素时,您可能会遇到sistating的问题,而沮丧的问题是 不明确的。这个令人困惑的错误可能源于代码结构中的常见监督。在提供的代码片段中理解错误,脚本文件(jQuery和您的脚本)位于HTML文档的部分中。结果,JS代码...
      编程 发布于2025-04-12
    • Java开发者如何保护数据库凭证免受反编译?
      Java开发者如何保护数据库凭证免受反编译?
      在java 在单独的配置文件保护数据库凭证的最有效方法中存储凭据是将它们存储在单独的配置文件中。该文件可以在运行时加载,从而使登录数据从编译的二进制文件中远离。使用prevereness class import java.util.prefs.preferences; 公共类示例{ 首选项...
      编程 发布于2025-04-12
    • 标准迭代器范围为何采用半开区间\[begin, end)而非闭区间\[begin, end\]?
      标准迭代器范围为何采用半开区间\[begin, end)而非闭区间\[begin, end\]?
      为什么标准迭代器范围[begin,end)而不是[begin,end]?对空序列的自然处理: 基于链范围的构造: the [begin,end,end)惯例,促进了基于多个嵌套范围的链接,而无需过时,并提升了一个杂货,并提醒了一个浪费的浪费,并提醒了一个浪费的杂物,并促进了基于型号的浪费。代码。在...
      编程 发布于2025-04-12
    • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
      为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
      查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
      编程 发布于2025-04-12
    • eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
      eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
      称量()和ast.literal_eval()中的Python Security 在使用用户输入时,必须优先确保安全性。强大的Python功能Eval()通常是作为潜在解决方案而出现的,但担心其潜在风险。 This article delves into the differences betwee...
      编程 发布于2025-04-12
    • 如何使用不同数量列的联合数据库表?
      如何使用不同数量列的联合数据库表?
      合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
      编程 发布于2025-04-12

    免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

    Copyright© 2022 湘ICP备2022001581号-3