useRef 钩子使用技巧

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
可观测可视化 Grafana 版,10个用户账号 1个月
可观测监控 Prometheus 版,每月50GB免费额度
简介: 【10月更文挑战第12天】本文详细介绍了 React 中的 `useRef` Hook,包括其基础概念、基本用法、常见问题与易错点以及如何避免这些问题。通过具体代码示例,解释了 `useRef` 的应用场景,如保存对 DOM 元素的引用、保存回调函数和定时器 ID 等,帮助开发者更有效地使用这一工具。

在 React 中,useRef 是一个非常有用的 Hook,它可以让你在组件的生命周期内保留一些数据,而不会引起组件的重新渲染。本文将从基础概念入手,逐步深入到 useRef 的常见问题、易错点及如何避免这些问题,并通过代码示例来帮助理解其应用场景和实现方式。
image.png

基础概念

什么是 useRef?

useRef 是一个 React Hook,它返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

基本用法

import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

export default TextInputWithFocusButton;

在这个例子中,useRef 用于获取对输入框的引用,以便在按钮点击时将其聚焦。

常见问题与易错点

1. 误用 useRef 来存储状态

useRef 不应该用来存储组件的状态。React 提供了 useStateuseReducer 来管理状态,而 useRef 主要用于保存那些不需要触发重新渲染的数据。

错误示例

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

function Counter() {
  const countRef = useRef(0);

  useEffect(() => {
    countRef.current++;
  });

  return (
    <div>
      Count: {countRef.current}
    </div>
  );
}

export default Counter;

在这个例子中,countRef 用于存储计数器的值,但由于 countRef 的变化不会触发重新渲染,因此界面上的计数器值不会更新。

正确示例

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

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

  useEffect(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <div>
      Count: {count}
    </div>
  );
}

export default Counter;

2. 忘记初始化 ref

在使用 useRef 时,忘记初始化 ref 可能会导致 undefined 错误。

错误示例

import React, { useRef } from 'react';

function Example() {
  const myRef = useRef();

  useEffect(() => {
    console.log(myRef.current.value); // 可能会抛出错误
  }, []);

  return (
    <input ref={myRef} type="text" />
  );
}

export default Example;

正确示例

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

function Example() {
  const myRef = useRef(null);

  useEffect(() => {
    console.log(myRef.current?.value); // 使用可选链操作符
  }, []);

  return (
    <input ref={myRef} type="text" />
  );
}

3. 在函数组件中使用 ref

在函数组件中使用 ref 时,需要确保正确地传递 ref

错误示例

import React, { useRef } from 'react';

function CustomInput(props) {
  return <input {...props} />;
}

function App() {
  const inputRef = useRef(null);

  return (
    <CustomInput ref={inputRef} />
  );
}

export default App;

正确示例

import React, { useRef, forwardRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

function App() {
  const inputRef = useRef(null);

  return (
    <CustomInput ref={inputRef} />
  );
}

export default App;

如何避免这些问题

  1. 明确用途useRef 用于保存那些不需要触发重新渲染的数据,不要用它来管理状态。
  2. 初始化 ref:始终初始化 ref,避免 undefined 错误。
  3. 使用可选链操作符:在访问 ref 的属性时,使用可选链操作符(?.)来防止潜在的 undefined 错误。
  4. 正确传递 ref:在自定义组件中使用 forwardRef 来正确传递 ref

进阶用法

1. 保存回调函数

useRef 可以用于保存回调函数,以避免在每次渲染时都创建新的函数引用。

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

function UseRefCallbackExample() {
  const callbackRef = useRef(null);

  useEffect(() => {
    callbackRef.current = handleScroll;
  });

  const handleScroll = () => {
    console.log('Scrolled');
  };

  useEffect(() => {
    window.addEventListener('scroll', callbackRef.current);
    return () => {
      window.removeEventListener('scroll', callbackRef.current);
    };
  }, []);

  return (
    <div>
      Scroll down to see the effect.
    </div>
  );
}

export default UseRefCallbackExample;

2. 保存定时器 ID

useRef 可以用于保存定时器 ID,以便在组件卸载时清除定时器。

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

function TimerExample() {
  const timerIdRef = useRef(null);

  useEffect(() => {
    timerIdRef.current = setInterval(() => {
      console.log('Tick');
    }, 1000);

    return () => {
      clearInterval(timerIdRef.current);
    };
  }, []);

  return (
    <div>
      Timer is running...
    </div>
  );
}

export default TimerExample;

总结

useRef 是一个非常强大的 Hook,可以帮助你在组件的生命周期内保留一些数据,而不会引起组件的重新渲染。通过本文的介绍和代码示例,希望你能更好地理解和应用 useRef,并在实际开发中避免常见的问题和易错点。

目录
相关文章
|
7天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
9天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1569 10
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
14天前
|
存储 人工智能 开发工具
AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
只需要通过向AI助理提问的方式输入您的需求,即可瞬间获得核心流程代码及参数,缩短学习路径、提升开发效率。
1025 1
AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
|
14天前
|
人工智能 Serverless API
AI助理精准匹配,为您推荐方案——如何快速在网站上增加一个AI助手
通过向AI助理提问的方式,生成一个技术方案:在网站上增加一个AI助手,提供7*24的全天候服务,即时回答用户的问题和解决他们可能遇到的问题,无需等待人工客服上班,显著提升用户体验。
983 4
|
13天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
792 27
|
1天前
|
人工智能 关系型数据库 Serverless
1024,致开发者们——希望和你一起用技术人独有的方式,庆祝你的主场
阿里云开发者社区推出“1024·云上见”程序员节专题活动,包括云上实操、开发者测评和征文三个分会场,提供14个实操活动、3个解决方案、3 个产品方案的测评及征文比赛,旨在帮助开发者提升技能、分享经验,共筑技术梦想。
402 43
|
7天前
|
并行计算 PyTorch TensorFlow
Ubuntu安装笔记(一):安装显卡驱动、cuda/cudnn、Anaconda、Pytorch、Tensorflow、Opencv、Visdom、FFMPEG、卸载一些不必要的预装软件
这篇文章是关于如何在Ubuntu操作系统上安装显卡驱动、CUDA、CUDNN、Anaconda、PyTorch、TensorFlow、OpenCV、FFMPEG以及卸载不必要的预装软件的详细指南。
490 3
|
2天前
|
移动开发 JavaScript 前端开发
💻揭秘!如何用 Vue 3 实现酷炫的色彩魔方游戏✨
本文分享了开发基于Canvas技术的小游戏"色彩魔方挑战"的完整过程。游戏旨在考验玩家的观察力和耐心,通过随机生成的颜色矩阵和一个变化点,玩家需在两幅画布中找出不同的颜色点。文章详细讲解了游戏的核心功能,包括随机颜色矩阵生成、点的闪烁提示、自定义配色方案等。此外,作者展示了使用Vue 3和TypeScript开发的代码实现,带领读者一步步深入了解游戏的逻辑与细节。
105 68
|
16天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
869 5