React ref & useRef 完全指南

简介: React ref & useRef 完全指南

在这篇文章中,你将学习如何使用React.useRef()钩子来创建持久的可变值(也称为references或refs),以及访问DOM元素。

我们将从下面几点讲解:

1. 可变值
- 1.1用例:记录按钮点击
- 1.2用例:实现秒表
2. 访问DOM元素
- 2.1用例:聚焦输入
3.更新引用限制
4. 总结

可变值

useRef(initialValue)接受一个参数(引用的初始值)并返回一个引用(也称为ref)。引用只是一个具有特殊属性current的对象:

const reference = useRef(initialValue);
reference.current; // 当前的引用

reference.current 访问引用,并且 reference.current = newValue 更新引用值:

import { useRef } from 'react';
function MyComponent() {
  const reference = useRef(initialValue);
  const someHandler = () => {
    // 访问引用
    const value = reference.current;
    // 更新引用值
    reference.current = newValue;
  };
}

关于 references 有两点需要记住:

  1. 在组件重新渲染之间,引用的值是持久化的(保持不变);
  2. 更新引用不会触发组件重新呈现。

现在,让我们看看如何在实践中使用 useRef()

实例:记录按钮点击

组件logbuttonclicked使用了一个引用来存储按钮的点击次数:

import { useRef } from 'react';
function LogButtonClicks() {
  const countRef = useRef(0);
  const handle = () => {
    countRef.current++;
    console.log(`Clicked ${countRef.current} times`);
  };
  console.log('I rendered!');
  return <button onClick={handle}>Click me</button>;
}

const countRef = useRef(0)创建一个用0初始化的引用countRef

当按钮被单击时,handle函数被调用,并且引用值被递增:countRef.current++,该引用值被记录到控制台。

注意,更新引用值countRef.current++不会触发组件重新渲染。

'I rendered!'在初始渲染时只会输出一次。

现在有一个合理的问题:引用和状态之间的主要区别是什么?

现在有一个合理的问题:referencesstate之间的主要区别是什么?

reference 和 state 之间的主要区别

让我们重用上一节中的logbuttonclicked组件,但使用useState()钩子来计算按钮的点击次数:

import { useState } from 'react';
function LogButtonClicks() {
  const [count, setCount] = useState(0);
  const handle = () => {
    const updatedCount = count + 1;
    console.log(`Clicked ${updatedCount} times`);
    setCount(updatedCount);
  };
  console.log('I rendered!');
  return <button onClick={handle}>Click me</button>;
}

每次点击,你会在控制台中看到“I rendering !”’——这意味着每次状态更新时,组件都会重新呈现

所以,state和references之间的两个主要区别是:

  1. 更新 state 会触发组件重新呈现,而更新 ref 则不会。
  2. state 更新是异步的(state变量在重新呈现后更新),而ref则同步更新(更新后的值立即可用)

从更高的角度来看,ref 用于存储组件的基础设施数据,而 state 存储直接呈现在屏幕上的信息

实例:实现秒表

你可以存储在 ref 中的东西是涉及到一些副作用的基础设施信息。例如,你可以在ref中存储不同类型的指针:定时器id,套接字id,等等。

例如,下面的秒表组件使用setInterval(回调,时间)计时器函数来增加秒表计数器的每一秒。定时器id存储在一个引用timerIdRef:

import { useRef, useState, useEffect } from 'react';
function Stopwatch() {
  const timerIdRef = useRef(0);
  const [count, setCount] = useState(0);
  const startHandler = () => {
    if (timerIdRef.current) { return; }
    timerIdRef.current = setInterval(() => setCount(c => c+1), 1000);
  };
  const stopHandler = () => {
    clearInterval(timerIdRef.current);
    timerIdRef.current = 0;
  };
  useEffect(() => {
    return () => clearInterval(timerIdRef.current);
  }, []);
  return (
    <div>
      <div>Timer: {count}s</div>
      <div>
        <button onClick={startHandler}>Start</button>
        <button onClick={stopHandler}>Stop</button>
      </div>
    </div>
  );
}

startHandler()函数在单击Start按钮时调用,它启动计时器并在引用timerIdRef.current= setInterval(…)中保存计时器id。

要停止秒表,请单击“停止”按钮。停止按钮处理程序stopHandler()从引用中访问计时器id并停止计时器clearInterval(timerIdRef.current)

此外,如果组件在秒表处于活动状态时卸载,useEffect()的清理函数也将停止计时器。

在秒表示例中,ref用于存储基础架构数据—活动计时器id。

访问 DOM 元素

useRef()钩子的另一个有用的应用是访问DOM元素。这需要三个步骤:

  1. 定义访问元素const elementRef = useRef()的引用;
  2. 将引用赋给元素的ref属性:<div ref={elementRef}></div>;
  3. 引用完成后,elementRef.current包含DOM元素。
import { useRef, useEffect } from 'react';
function AccessingElement() {
  const elementRef = useRef();
   useEffect(() => {
    const divElement = elementRef.current;
  }, []);
  return (
    <div ref={elementRef}>
      I'm an element
    </div>
  );
}

实例:聚焦输入框

import { useRef, useEffect } from 'react';
function InputFocus() {
  const inputRef = useRef();
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (
    <input 
      ref={inputRef} 
      type="text" 
    />
  );
}

const inputRef = useRef()创建一个引用来保存输入元素。

然后将inputRef赋值给输入字段的ref属性:<input ref={inputRef} type="text" />

然后,设置inputRef 作为输入元素。现在您可以通过编程方式将焦点设置为输入状态:inputRef.current.focus()

在初始化渲染时 Ref 是 null

在初始渲染时,保存DOM元素的 ref 是空的:

import { useRef, useEffect } from 'react';
function InputFocus() {
  const inputRef = useRef();
  useEffect(() => {
    // 输出 `HTMLInputElement` 
    console.log(inputRef.current);
    inputRef.current.focus();
  }, []);
  // 初始化渲染时输出 `undefined` 
  console.log(inputRef.current);
  return <input ref={inputRef} type="text" />;
}

在初始渲染期间,React仍然决定组件的输出,因此还没有创建DOM结构。这就是为什么inputRef。current在初始呈现时计算为undefined

当输入元素在DOM中创建完成后,useEffect(callback,[])钩子立即调用回调函数:因此回调函数是访问inputRef.current的正确位置。

更新 references 限制

功能组件的功能范围应该计算输出或调用钩子。

这就是为什么更新 ref (以及更新 state)不应该在组件函数的直接作用域内执行。

ref必须在useEffect()回调或处理程序(事件处理程序、计时器处理程序等)内部更新

import { useRef, useEffect } from 'react';
function MyComponent({ prop }) {
  const myRef = useRef(0);
  useEffect(() => {
    myRef.current++; // Good!
    setTimeout(() => {
      myRef.current++; // Good!
    }, 1000);
  }, []);
  const handler = () => {
    myRef.current++; // Good!
  };
  myRef.current++; // Bad!
  if (prop) {
    myRef.current++; // Bad!
  }
  return <button onClick={handler}>My button</button>;
}

总结

useRef()钩子存储可变的值(又名references或refs),这些值在渲染之间持久化,也可以访问DOM元素。

使用初始值调用const reference = useRef(initialValue)会返回一个名为reference的特殊对象。引用对象有一个属性current:可以使用该属性读取引用值,或更新引用。reference.current = newValue

在组件重新呈现之间,引用的值是持久的。

更新引用与更新状态相反,不会触发组件重新呈现。

引用也可以访问DOM元素。<div ref={reference}> element </div> - 元素在reference.current中是可用的。


相关文章
|
2月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
68 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
2月前
|
前端开发 JavaScript
React中函数式Hooks之useRef的使用
React中函数式Hooks的useRef用于获取DOM元素的引用,示例代码演示了其基本用法。
34 3
|
2月前
|
JavaScript 前端开发 开发者
React 的正确使用方法:ref 篇
你真的用对了 useRef 吗?在与 TypeScript 一起使用、以及撰写组件库的情况下,你的写法能够避开以下所有场景的坑吗?
|
3月前
|
前端开发
React使用useRef ts 报错
【8月更文挑战第17天】
67 4
|
27天前
|
存储 缓存 JavaScript
深入理解 React-Hooks 之 useRef
【10月更文挑战第20天】总之,`useRef` 是 React-Hooks 中一个非常实用的工具,它为我们提供了一种灵活而强大的方式来处理组件中的各种数据和操作。通过深入理解和掌握 `useRef` 的原理和用法,我们能够更好地构建高效、稳定的 React 应用。
32 6
|
1月前
|
存储 前端开发 JavaScript
React useState 和 useRef 的区别
本文介绍了 React 中 `useState` 和 `useRef` 这两个重要 Hook 的区别和使用场景。`useState` 用于管理状态并在状态变化时重新渲染组件,适用于表单输入、显示/隐藏组件、动态样式等场景。`useRef` 则用于在渲染之间保持可变值而不触发重新渲染,适用于访问 DOM 元素、存储定时器 ID 等场景。文章还提供了具体的代码示例,帮助读者更好地理解和应用这两个 Hook。
40 0
|
2月前
|
前端开发
react学习(17)回调形式的ref
react学习(17)回调形式的ref
|
2月前
|
存储 前端开发 容器
react学习(18)createRef形式的ref
react学习(18)createRef形式的ref
|
2月前
|
存储 前端开发 JavaScript
react的useRef用什么作用
react的useRef用什么作用
49 1
|
3月前
|
前端开发 知识图谱
[译] React 中的 "最新 Ref 模式"
[译] React 中的 "最新 Ref 模式"