useRef 钩子使用技巧

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 【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,并在实际开发中避免常见的问题和易错点。

目录
相关文章
|
11月前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
213 4
|
关系型数据库 MySQL 分布式数据库
PolarDB 与传统数据库的性能对比分析
【8月更文第27天】随着云计算技术的发展,越来越多的企业开始将数据管理和存储迁移到云端。阿里云的 PolarDB 作为一款兼容 MySQL 和 PostgreSQL 的关系型数据库服务,提供了高性能、高可用和弹性伸缩的能力。本文将从不同角度对比 PolarDB 与本地部署的传统数据库(如 MySQL、PostgreSQL)在性能上的差异。
879 1
|
11月前
|
前端开发 JavaScript
useReducer 钩子实战
【10月更文挑战第13天】在 React 中,`useState` 是常用的状态管理钩子,但面对复杂状态逻辑时,`useReducer` 提供了更结构化的方式。本文从基础到进阶介绍 `useReducer` 的使用方法、常见问题及解决方案,并通过计数器和表单组件的示例加深理解。
205 6
|
12月前
|
JavaScript 前端开发 中间件
Redux从入门到进阶,看这一篇就够了!
该文章全面介绍了Redux的基本概念与使用方法,从Redux的安装配置到结合React应用的状态管理,再到中间件如Redux-thunk的使用,帮助读者从零开始掌握Redux在复杂应用中的实践应用。
|
存储 人工智能 关系型数据库
MySQL 8.0 字符集与比较规则介绍
我们都知道 MySQL 8.0 与 MySQL 5.7 的区别之一就是默认字符集从 latin1 改成了 utf8mb4 ,除此之外,MySQL 8.0 下的字符集和比较规则还有没有其他变化呢?本篇文章我们一起来学习下。
779 1
|
缓存 前端开发 JavaScript
理解 React 的 Fiber 架构
【8月更文挑战第6天】 理解 React 的 Fiber 架构
715 1
|
11月前
|
资源调度 前端开发 测试技术
React Router 路由管理
【10月更文挑战第10天】本文介绍了 React Router,一个在 React 应用中管理路由的强大工具。内容涵盖基本概念、安装与使用方法、常见问题及解决方案,如路由嵌套、动态路由和路由守卫等,并提供代码示例。通过学习本文,开发者可以更高效地使用 React Router,提升应用的导航体验和安全性。
754 19
|
10月前
|
资源调度 前端开发 JavaScript
React 测试库 React Testing Library
【10月更文挑战第22天】本文介绍了 React Testing Library 的基本概念和使用方法,包括安装、基本用法、常见问题及解决方法。通过代码案例详细解释了如何测试 React 组件,帮助开发者提高应用质量和稳定性。
335 0
|
12月前
|
JSON 前端开发 JavaScript
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识
该文章提供了Webpack的基础入门指南,涵盖安装配置、基本使用、加载器(Loaders)、插件(Plugins)的应用,以及如何通过Webpack优化前端项目的打包构建流程。
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识