新闻中心

深入理解React useRef与数组操作:避免常见陷阱

2025-10-27
浏览次数:
返回列表

深入理解React useRef与数组操作:避免常见陷阱

在使用react的`useref`管理非渲染数据时,对存储在其中的数组进行过滤操作是一个常见场景。本文将详细阐述为何`array.prototype.filter()`方法无法直接修改`useref`中存储的数组,并提供正确的更新策略,同时纠正了访问`useref`长度的常见错误,确保数据管理和逻辑判断的准确性。

理解 useRef 与 J*aScript 数组的不可变性

在React应用中,useRef是一个非常有用的Hook,它允许我们在组件的整个生命周期中持久化一个可变的值,而不会触发组件的重新渲染。这使得它非常适合存储不直接影响UI的数据,例如DOM元素的引用、计时器ID或者本例中讨论的非渲染数据数组。

然而,当我们在useRef中存储一个数组并尝试对其进行修改时,一个常见的误解是认为像filter()这样的数组方法会原地修改原始数组。实际上,J*aScript中的许多数组方法,包括filter()、map()、slice()等,都是不可变的。这意味着它们不会修改调用它们的原始数组,而是返回一个包含新结果的新数组

考虑以下代码片段,它试图从useRef中存储的items.current数组中移除一个元素:

let items = useRef([]); // 假设 items.current 已经包含一个数组
// ...
// 尝试过滤掉名为 'toy' 的项目
items.current.filter((item) => item.name !== toy);
// 此时,items.current 并没有被修改!

这段代码的问题在于,filter()方法执行后,会返回一个不包含指定toy的新数组,但这个新数组并没有被赋值回items.current。因此,items.current仍然保持着过滤前的原始数组。

正确更新 useRef 中存储的数组

要正确地更新useRef中存储的数组,我们需要将filter()方法返回的新数组显式地赋值回items.current。

import { useN*igate } from 'react-router-dom';
import { useState, useEffect, useRef } from "react";
import supabase from "../config/supabaseClient";
import Image from "./image";
import Timer from "./timer";

const Game = () => {
  let items = useRef([]); // 使用 useRef 存储非渲染数据
  const [fetchError, setFetchError] = useState(null);
  const [found, setFound] = useState("");
  const [time, setTime] = useState(0);
  const n*igate = useN*igate();

  useEffect(() => {
    const fetchOptions = async () => {
      const { data, error } = await supabase
        .from('items')
        .select();

      if (error) {
        setFetchError('Could not fetch items');
        items.current = [];
      }

      if (data) {
        items.current = data;
        setFetchError(null);
      }
    }
    fetchOptions();
  }, []);

  function handleAction(click, toy) {
    const item = items.current.find(item => item.name === toy);

    if (!item) {
      setFound(`Not quite, try again!`);
      return;
    }

    if (click.x > item.left && click.x < item.right) {
      if (click.y < item.bottom && click.y > item.top) {
        setFound(`Well done! You've found Sarah's ${toy}`);
        // 正确的做法:将过滤后的新数组重新赋值给 items.current
        items.current = items.current.filter((i) => i.name !== toy);
        console.log("Updated items.current:", items.current); // 检查更新后的数组

        // 检查数组长度时,也需要访问 items.current
        if (items.current.length === 0) {
          console.log('Winner');
          n*igate("/leaderboard", { state: time });
        }
      }
    } else {
      setFound(`Not quite, try again!`);
      return;
    }
  }

  return (
    <>
      {fetchError && (<p>{fetchError}</p>)}
      <Timer time={time} setTime={setTime} />
      <Image handleAction={handleAction} />
      <p>{found}</p>
    </>
  );
}

export default Game;

在上述代码中,关键的修改是这一行:

小爱开放平台 小爱开放平台

小米旗下小爱开放平台

小爱开放平台 291 查看详情 小爱开放平台
items.current = items.current.filter((i) => i.name !== toy);

通过将filter()方法返回的新数组赋值回items.current,我们确保了useRef中存储的数据得到了正确的更新。

另一个常见陷阱:访问 useRef 的长度

除了过滤操作,另一个与useRef相关的常见错误是直接访问ref对象的length属性,而不是其current属性的length。

错误的示例:

if (items.length === 0) { // 错误!items 是一个 ref 对象,不是数组
  console.log('Winner');
  n*igate("/leaderboard", { state: time });
}

items本身是一个RefObject,它不具备length属性(除非你把它赋值为一个带有length属性的对象,但这与我们的意图不符)。我们真正想要检查的是useRef所引用的数组的长度,因此必须通过items.current来访问。

正确的做法:

if (items.current.length === 0) { // 正确!访问 ref 引用值的长度
  console.log('Winner');
  n*igate("/leaderboard", { state: time });
}

总结与注意事项

  1. filter() 不会原地修改数组: Array.prototype.filter()方法总是返回一个新数组,而不会修改原始数组。要更新数据,必须将新数组重新赋值。
  2. useRef 的值通过 .current 访问和修改: 无论是读取还是写入useRef所引用的值,都必须通过其.current属性进行。直接操作ref对象本身(例如items.length)通常是错误的。
  3. 何时使用 useRef vs useState: 当数据不需要触发组件重新渲染时,useRef是管理可变数据的理想选择。如果数据的变化需要反映在UI上,则应使用useState。在本例中,游戏中的隐藏物品列表不直接渲染,因此useRef是合适的。

通过理解这些核心概念和避免常见陷阱,您可以更有效地在React应用中使用useRef来管理复杂的数据结构。

以上就是深入理解React useRef与数组操作:避免常见陷阱的详细内容,更多请关注其它相关文章!


# 的是  # 二维码推广营销文案模板  # 厦网站建设培训学校  # 重庆网站建设有佳科技  # 裕华区电商网站推广模式  # 亭湖区网站建设  # 网络营销推广有什么特点  # 苏州网站关键词排名优化  # 南通港闸区专业seo费用价格  # 设计素材网站建设  # 建设网站建设目的意义  # 本例  # 都是  # react  # 不直接  # 如何实现  # 服务端  # 自定义  # 数据结构  # 小爱  # 是一个  # gate  # win  # ai  # java  # javascript 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 极速漫画官方主页网址 极速漫画漫画在线浏览官网链接  poki网页游戏推荐_poki免费游戏平台入口  PPT平滑切换怎么做 PPT炫酷“平滑”切换动画制作教程【必学】  在Typer应用中优雅地处理和重组任意命令行参数  4399网页游戏电脑版全新入口 4399电脑端在线玩指南  windows10怎么关闭系统提示音_windows10彻底静音设置方法  Discord Slash 命令响应超时问题的异步解决方案  网易大神怎么保存别人动态的图片_网易大神动态图片保存方法  Windows10怎么开启存储感知 Windows10系统设置自动清理临时文件释放C盘空间【教程】  age动漫网站入口 age动漫官网直接访问入口  TikTok网页版直接登录 TikTok网页端官方平台入口  没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享  利用5118提升短视频内容效果_5118短视频关键词优化方法  如何有效阻止外部脚本意外修改内联样式的高度属性  腾讯视频怎么使用多账号家庭管理_腾讯视频家庭多账号统一管理与权限分配教程  Yandex搜索引擎官网入口_俄罗斯Yandex免登录一键直达  处理嵌套交互式控件:前端可访问性指南  Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组  顺丰国际快递查询 国际件官方查询入口  如何设置Windows Defender的定时扫描_计划任务实现自动杀毒【安全】  照顾宝贝2小游戏免费秒玩入口  J*aScript中赋值与自增运算符的复杂交互与执行机制  Excel函数批量查找替换超快方法_Excel用REPLACE和FIND函数秒级替换  Node.js中HTML按钮与J*aScript函数交互的正确姿势  Win11怎么开启高性能模式_Windows 11电源计划优化设置  vivo手机互传视频怎么操作_vivo手机互传视频详细传输方法  写好的html代码怎么运行出来_运行写好的html代码方法【教程】  Python自定义类排序:解决lambda键值访问TypeError的实践指南  整合Supabase认证与Django模型:跨模式迁移的解决方案  构建轻量级网站内部消息系统:Formspree 集成指南  解决Tabulator日期时间排序问题的专业指南  C++编译期如何执行复杂计算_C++模板元编程(TMP)技巧与应用  C#如何安全地从用户上传的XML文件中读取数据? 验证与清理策略  包子漫画官方网站阅读入口-包子漫画在线漫画官网直达链接  AngularJS $http POST请求数据传递与Go后端接收实践  网站内容防复制粘贴的实现策略与局限性  “音游” × “怪文书” 题材的节奏冒险游戏 《晕晕电波症候群》确定于2026年4月发售!  Python:递归比较文件夹内容并找出特定类型文件的差异  HTML空白字符处理机制:渲染、DOM与编码实践  圆通快递查询实时追踪 圆通物流包裹状态快速查看  手机屏幕碎了但能正常使用怎么办 手机外屏碎裂的修复建议  HuggingFaceEmbeddings中向量嵌入维度调整的限制与理解  html5 app怎么运行环境_配html5 app运行环境【教程】  天猫双十一预售商品怎么退款_天猫双十一预售退款操作指南  R星幕后开发视频泄露 包含《GTA6》等多款大作  谷歌浏览器如何快速清除某个网站的数据_Chrome网站缓存清理方法  sublime如何配置Go语言开发环境_sublime搭建Golang编译运行系统  微信商城在哪里打开【步骤】  格力空气能E5故障代码是什么情况_格力空气能E5代码解析与应对措施  必由学官网首页入口 必由学教师网页版登录指南 

搜索