useRef 钩子使用技巧

简介: 【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,并在实际开发中避免常见的问题和易错点。

目录
相关文章
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
627 2
|
安全 Java API
2023 年 SpringBoot 学习路线(一)
2023 年 SpringBoot 学习路线(一)
|
Java 编译器 开发者
java中运行时异常与编译时异常?
java中运行时异常与编译时异常?
|
6月前
|
SQL XML Java
Mybatis基础使用知识(注解)
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。 最后 mybatis 框架执行sql 并将结果映射为java对象并返回。采用ORM(对象关系映射)思想解决了实体和数据库映射问题,对jdbc进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api 打交道,就可以完成对数据库的持久化操作。
481 0
|
JavaScript 前端开发 中间件
Redux从入门到进阶,看这一篇就够了!
该文章全面介绍了Redux的基本概念与使用方法,从Redux的安装配置到结合React应用的状态管理,再到中间件如Redux-thunk的使用,帮助读者从零开始掌握Redux在复杂应用中的实践应用。
|
安全 算法 Java
Java“SSLException”错误解决
Java“SSLException”错误通常发生在SSL/TLS连接过程中,可能是由于证书问题、握手失败或加密套件不匹配等原因引起。解决方法包括检查服务器证书、配置信任库、确保JDK版本兼容等。
2774 4
|
JavaScript 前端开发 API
你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解
onMounted作为vue3中最常用的钩子函数之一,能够灵活、随心应手的使用是每个Vue开发者的必修课,同时根据其不同写法的特性,来选择最合适最有利于维护的写法。博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
Java Linux iOS开发
如何配置 Java 环境变量:设置 JAVA_HOME 和 PATH
本文详细介绍如何在Windows和Linux/macOS系统上配置Java环境变量。
18184 12
|
Web App开发 前端开发 JavaScript
WebKit 入门介绍
WebKit 入门介绍
|
前端开发 JavaScript
JS-instanceof 的实现原理
`instanceof` 运算符在前端 JavaScript 中用于检测对象的原型链是否包含指定构造函数的 `prototype` 属性。它通过遍历对象的原型链来实现。每个对象都有一个内部链接 `[[Prototype]]` 指向其原型对象,当访问属性或方法时,JavaScript 引擎会沿着原型链查找。`instanceof` 的具体实现是通过比较对象的原型链中的原型与构造函数的 `prototype` 属性,直到找到匹配的原型或到达原型链的顶端。示例代码展示了如何使用 `instanceof` 检查对象的继承关系。此外,`instanceof` 可用于验证继承关系和类型检查,支持多态性。