为什么需要 useRef 而不是可变变量?

2023-11-23

我读过了useEffect 完整指南 - 逆流而上反应过度。

该示例表明,如果我们想获取最新的count, 我们可以用useRef保存可变变量,并在异步函数中获取它:

function Example() {
  const [count, setCount] = useState(0);
  const latestCount = useRef(count);

  useEffect(() => {
    // Set the mutable latest value
    latestCount.current = count;
    setTimeout(() => {
      // Read the mutable latest value
      console.log(`You clicked ${latestCount.current} times`);
    }, 3000);
  });
  // ...
}

但是,我可以通过在组件函数外部创建变量来完成相同的操作,例如:

import React, { useState, useEffect, useRef } from 'react';

// defined a variable outside function component
let countCache = 0;

function Counter() {
  const [count, setCount] = useState(0);
  countCache = count;       // set default value

  useEffect(() => {
    setTimeout(() => {
      // We can get the latest count here
      console.log(`You clicked ${countCache} times (countCache)`);
    }, 3000);
  });
  // ...
}

export default Counter;

这两种方法都实用吗?或者如果我在函数组件之外定义变量会有什么不好吗?


useRef将分配一个参考each组件,而在函数组件范围之外定义的变量只会分配一次.

useRef引用生命周期是组件的生命周期(当组件卸载时它“死亡”,而 JS 变量是范围阻塞的)。

因此,在组件范围之外定义常量目的变量:

// This statement will only called once
const DEFAULT_VALUE = 5;

function Component() {
  // use DEFAULT_VALUE.
}

在组件范围内定义相同的语句,将在每次渲染时重新定义它:

// We can do better
function Component() {
  // Redefined on every render
  const DEFAULT_VALUE = 5;
}

现在问题是:

首先,我们实际上无法反映使用外部作用域变量更改的 UI,因为更改它们不会触发渲染(只有 React API 会触发渲染)。

因此反射值就是它的closure value.

let countCache = 0;

function Counter() {
  ...
  countCache = 0;

  useEffect(() => {
    countCache = count;
  });
  ...

  // closure value of countCache
  return <div>{countCache}</div>
}

现在,外部作用域变量的特殊之处在于它们对于模块本身来说是全局的,因此使用它的值对于引用它的所有组件(在模块中)来说是全局的。

例如,如果您想计算组件在整个应用程序生命周期内安装的次数,请增加内部变量useEffect安装时(找不到任何其他可能的用例)。

let howMuchMounted = 0;

function Component() {
  useEffect(() => { howMuchMounted += 1, [] };
}

反映外变量与useRef参考,在下一个示例中,单击按钮时,您可能会注意到variable对于两个组件来说都是全局的,而reference始终更新为当前状态值。

import React, { useEffect, useRef, useReducer } from "react";
import ReactDOM from "react-dom";

// defined a variable outside function component
let countCache = 0;

function Counter() {
  const [num, count] = useReducer((num) => num + 1, 0);

  const countRef = useRef(count);

  useEffect(() => {
    // set count value on count change
    countCache = num;
    countRef.current = num;
  }, [num]);

  return (
    <>
      <button onClick={count}>Count</button>
      <h3>state {num}</h3>
      <h3>variable {countCache}</h3>
      <h3>reference {countRef.current}</h3>
    </>
  );
}

export default function App() {
  return (
    <>
      <Counter />
      <hr />
      See what happens when you click on the other counter
      <hr />
      <Counter />
    </>
  );
}

请看一个跟进问题useEffect用例,在使用时有很多常见的错误useRef里面的参考文献useEffect.

Edit useRef vs Variable

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么需要 useRef 而不是可变变量? 的相关文章

随机推荐

  • Internet Explorer 自动切换到兼容模式(IE9 和 IE10)

    在我网站的某些页面上 Internet Explorer 自动切换到兼容模式并尝试在兼容视图 IE7 模式 中呈现该页面 URL 也会添加到兼容性视图列表中 就我而言 这是由于某些 CSS 使用 Type 1 字体 黑体 Internet
  • 如何放心地登录到文本文件中可打印的内容

    我正在研究一种方法 尝试使用 log4j 将默认的放心日志 转到控制台 更改为文件 这是一个 JUnit 项目 其方法最终调用 REST 外观 该外观具有类似这样的方法 private ResponseSpecification respo
  • Dask For 并行循环

    我正在尝试找到使用带有 dask 延迟的 for 循环的正确语法 我找到了几个教程和其他问题 但没有一个适合我的条件 这是非常基础的 首先 这是并行运行 for 循环的正确方法吗 time list names a b c d keep r
  • 如何以角度2分割字符串

    我有一个电子邮件发送场景 其中有一个 收件人 输入框 您要向其发送消息 app html
  • 无法在 Eclipse 中将库添加到 Android 项目

    我正在尝试将库添加到 eclipse 内的现有项目中 我将项目文件夹本身和库文件夹保存在桌面上标记为 ANDROID 的文件夹中 当我尝试导入 gt 现有代码到工作区 gt 选择 ANDROID 时 唯一添加的项目是应用程序本身 而不是库
  • iPhone - [NSUserDefaults standardUserDefaults] 文件存储在计算机上的哪里?

    在 iPhone 上运行应用程序时 例如 当您可以以另一种方式测试某些 GPS 或相机功能时 我在哪里可以找到并检查 NSUserDefaults 创建的文件以保存 standardUserDefaults 我正在运行 XCode 4 它位
  • 如何使用调查包计算比例?

    这只是一个非常简单的问题 但我只是无法从网络和书籍中找到合适的函数来使用 这是我从这里的一篇文章中得到的一个例子 df lt data frame sex c F M F M M M F F married c 1 1 1 1 0 0 1
  • 以编程方式检查 Windows 应用商店应用程序更新

    我实际上正在尝试找到一种方法来检查 Windows 应用商店应用程序是否有可用更新 有没有办法用 API 来做到这一点 谢谢 埃萨姆 Windows 应用商店没有任何专门用于检查更新的 API 有一个相当简单的解决方法 var packag
  • libc6:i386 和 libc6-i386 有什么区别

    我在用着Ubuntu 14 04 2 LTS n l 这里有libc我安装了 dpkg list grep libc6 ii libc6 amd64 2 19 0ubuntu6 7 amd64 Embedded GNU C Library
  • MongoDB 聚合/组/求和查询转换为 pymongo 查询

    我有一组条目goals集合看起来像这样 user adam position attacker goals 8 user bart position midfielder goals 3 user cedric position goalk
  • 如何使用 NodeJS 列出 GCS 存储桶中的目录

    I 如果您正在使用 NodeJS GCS 客户端库并想要列出存储桶中的目录 您该怎么做 首先将 NodeJS GCS 客户端库的依赖项添加到您的package json通过运行以下命令创建文件 npm i google cloud stor
  • .NET 垃圾收集器遇到麻烦。阻塞 15-40 分钟

    一些事实 我们开发了 wcf 服务 充当客户端和数据库之间的层 它是自托管的并作为 Windows 服务运行 该服务保留了多个缓存 其中最大的缓存大约为 1 2GB 总内存使用量通常约为 5 8GB 连接是双工的 使用 tcp 协议 序列化
  • eval() 不在运行时分配变量

    I use eval 将列表分配给 var eval mylist 1 2 3 但是当我运行它时 我得到了一个语法错误 它出什么问题了 如果我不能在eval 如何在运行时分配 var Use exec对于声明 gt gt gt exec l
  • 应用程序配置错误 抱歉,myapp 尚未获准在应用程序中心显示。在 Android 应用程序中共享

    当我在 Facebook 上点击我的应用程序名称时 它重定向到 https www facebook com games app id 827708240586758 并显示 应用程序配置错误抱歉 我的应用程序尚未获准在应用程序中心显示 我
  • 如何在不使用故事板的情况下创建 UICollectionView?

    我在网上找到了一些不错的 UIcollectionview 教程 http www raywenderlich com 22324 beginning uicollectionview in ios 6 part 12 http ashfu
  • AppBarLayout 以编程方式更改偏移量

    如何以编程方式更改 AppBarLayout 的偏移量 当 Activity 首次加载时 我希望 AppBarLayout 部分展开 有一定的偏移量 然后用户可以进一步展开它或折叠它 当前的行为是当 Activity 首次加载时它完全展开
  • 交换 gnuplot 中的轴

    我想知道这个有一段时间了 它可能已经在gnuplot但我一直无法在网上找到信息 当您有数据文件时 可以交换轴并将 虚拟变量 例如 x 在 gnuplot 的帮助术语中 分配给垂直轴 plot data u 1 2 x goes to hor
  • 使用事务进行rails数据库迁移

    我刚刚学习 Rails 并开始了有关数据库迁移的部分 我构建了 2 个迁移 并且都成功迁移了 向下迁移 最新的迁移 即第一个运行的迁移 由于我的代码中的拼写错误而失败 我修复了拼写错误 但此后迁移仍然失败 我发现原因是向下迁移在更改中途中止
  • Laravel 5.6 中的 url() 与 Route()

    就我而言 Laravel 5 6 中的 url 和 route 有什么区别 下面给出了两个 URI a href Create post 1 a and a href Create post 2 a 我在 web php 中定义它们如下 R
  • 为什么需要 useRef 而不是可变变量?

    我读过了useEffect 完整指南 逆流而上反应过度 该示例表明 如果我们想获取最新的count 我们可以用useRef保存可变变量 并在异步函数中获取它 function Example const count setCount use