useRef

先来看看官网如何解释:

你应该熟悉 ref 这一种访问 DOM的主要方式。如果你将 ref 对象以 <div ref={myRef} /> 形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点。
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在.
它创建的是一个普通 Javascript 对象。而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。

官网的解释我将其归纳为以下几点:

  1. 赋给DOM的ref属性,可以用来访问该DOM节点
  2. 返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
  3. 返回的 ref 对象在组件的整个生命周期内保持不变
  4. 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
  5. useRef 类似于类组件的 this

关于第1点

先来看看如何利用useRef获取DOM节点

import { useRef} from "react";
import * as ReactDOM from "react-dom";

function App() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
    // inputEl.current.value = 'abc';
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
GIF 2021-12-14 17-32-31.gif

当我点击右侧的按钮,左侧的输入框就聚焦了。

关于第3、4点

有如下场景:这是我写的一个AutoComplete组件,handleChange是在正常输入的时候改变value的值而触发搜索的事件函数,handleSelect函数是当我点击搜索结果列表的其中一项后将列表值插入到输入框中,但这个操作同样也改变了value的值从而导致触发搜索——很明显这次搜索是不必要的。这个时候我试图使用一个state来作为标识符(flag),当正常输入(执行handleChange)的时候set其为true,当点击列表(执行handleSelect)的时候set其为false,然后在useEffect中根据它的值来判断是否要执行搜索。这个解决方案本身没有任何问题,足矣解决我的问题。但,我们知道,一个state的改变是会触发组件渲染的,而这个标识符却没有任何触发渲染的必要,它仅仅需要起到一个标识的作用而已!
此时,就用到了useRef的第3、4点特性。
我使用useRef初始化了后给到triggerSearch变量,在handleChangetriggerSearch.current设置为true,在handleSelect中将triggerSearch.current设置为false,然后在useEffect中使用它拦截即可。
useRef不会随组件的重新渲染而被重置,同时它的改变也不会触发组件的重新渲染。

image.png

关于第5点

直接看如下Demo:

import React, { useState,  useRef } from "react";
import ReactDOM from "react-dom";

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

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);

运行结果如下:

GIF 2021-12-22 17-13-37.gif

当我在点击show alert按钮后接着在Click me按钮点击了3次,alert出的count仍然是0。产生这个现象的原因涉及到event loop的概念:setTimeout在事件队列中形成一个新的任务(宏任务),其回调函数是其微任务,当我点击show alert按钮的时候setTimeout中拿到初始渲染时声明的count,在回调函数执行的时候也不会改变。之后点击Click me按钮后count被重新创建,与setTimeout中访问的最初那个count不再有任何关系,因此setTimeout中的那个count就是0。

useRef的第5点特性正是可用来解决这个问题。
useRef创建的对象能够在组件中始终保留来存储某些东西(就像class组件中的this对象一样)即使组件重新渲染也不会被改变。

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

  const currentCount = useRef();
  currentCount.current = count;
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + currentCount.current);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

GIF 2021-12-22 17-46-10.gif

代码中currentCount对象始终存在于组件中,即使组件发生重新渲染,它始终保留着。那么setTimeout的回调函数拿到的始终都是同一个存储着最新countcurrentCount

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容