React-Hooks 初识 (三): useRef 的使用:获取DOM元素和保存变量
写在前面:万年鸽王回来了😁😁😁,记得我上次更文还是在上次。本想着过完年好好输出输出,结果这一歇就把人歇懒了。害,还是要时刻保持学习呐,更文不能停!!! 上一篇useEffect里我说了要解释下capture value 特性,这个其实一句话来说就是拿了 props或者state的快照,导致每次要做处理的时候都是用的某一刻state的快照在处理,这样就导致我们获取不到最新的值来进行更新页面。解决办法也简单:一种是加依赖项;另一种就是利用本篇useRef的一个特性:在任一地方取useRef保存的值都是取最新的!!好了,废话不多说,进入本次useRef的学习🐻🐰
const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
补充: useRef 设定的值进行重新赋值时是不会引发组件的重新渲染的。如果变量不涉及页面展示只是用来查询值时,此变量就可以用 useRef 进行保存;例如:在进行数据请求时,我们往往要进行处理入参,此时入参就可以用useRef进行保存。有小伙伴可能会问为什么不用state来进行设置。这里我们要注意区分何时用state--->若我们设置的变量是需要在页面渲染展示的、或者我们设置的此值变化要引起重新渲染时,我们再使用useState。否则,引入太多state会造成不必要的rerender。
1 useRef 的作用
-
用useRef获取React JSX中的DOM元素,获取后你就可以控制DOM的任何东西了。
-
用useRef来保存变量,注意:useRef 每次都会返回相同的引用
-
可以用来生成对 DOM 对象的引用。
2 useRef 获取DOM 元素
需求说明:界面上有一个文本框,在文本框的旁边有一个按钮,当我们点击按钮时,在控制台打印出input的DOM元素,并进行复制到DOM中的value上。这一切都是通过useRef来实现。
demo1: 这段代码的实现的功能就是点击button按钮将input框里输入的值展示到p标签内。
import React, { useState, useRef } from "react";
function App() {
let [name, setName] = useState("Nate");
let nameRef = useRef(); // 这里就是获取ref绑定的那个DOM元素值
const submitButton = () => {
setName(nameRef.current.value); / 这里用setName设置name值时,是把input框里的输入值传入进去
};
return (
<div className="App">
<p>{name}</p>
<div>
<input ref={nameRef} type="text" /> / 这里的ref就是获取了这个input输入框,利用ref.current就可以获取这个DOM节点
<button type="button" onClick={submitButton}>
Submit
</button>
</div>
</div>
);
}
=====================================================================
demo2: 使用 useRef 创建的变量指向一个 input 元素,点击按钮后使 input 聚焦
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>
</>
);
}
代码分析:useRef 返回的值传递给组件或者 DOM 的 ref 属性,就可以通过 ref.current 值访问组件或真实的 DOM 节点,从而可以对 DOM 进行一些操作,比如监听事件等等。这个利用useRef获取DOM元素的操作可以类比vue中利用ref属性进行相应的获取DOM元素或组件进行理解;
3 利用useRef 保存普通变量
代码片段
demo1: 利用useRef保存值
import React, { useRef, memo } from 'react';
const InnerTable = memo(function (props) {
const queryRef = useRef({
invoiceCode: '',
invoiceNumber: '',
});
return (
<input onChange={(e)=>{queryRef.current.invoiceCode=e.target.value}} placeholder="请输入发票代码"> </input>
<input onChange={(e)=>{queryRef.current.invoiceNumber=e.target.value}} placeholder="请输入发票号码"> </input>
)
})
这时候就可以实现每次状态修改,同时保存到useRef中了。也就是我们说的保存变量的功能。
3.1 useRef 可以绕过Capture Value 的特性 (非常重要): 也就是说,ref不会受到闭包影响,声明过ref后,在之后拿此ref值时拿到的都是最新的ref
注意: useRef 在 react hook 中的作用,就像一个盒子, 你可以存放任何东西,useRef 每次都会返回相同的引用;利用 useRef 可以绕过React Hooks 中的 Capture Value 的特性,可以认为 ref 在所有 Render 过程中保持着唯一引用,因此所有对 ref 的赋值或取值,拿到的都只有一个最新状态,而不会在每个 Render 间存在快照。
注意 :React Hooks 中存在 Capture Value 的特性:
function MessageDemo() {
const [message, setMessage] = useState("");
const showMessage = () => {
alert("You said: " + message);
};
const handleSendClick = () => {
setTimeout(showMessage, 3000);
};
const handleMessageChange = e => {
setMessage(e.target.value);
};
return (
<>
<input value={message} onChange={handleMessageChange} />
<button onClick={handleSendClick}>Send</button>
</>
);
}
对Capture Value 特性的分析:在点击 Send 按钮后,再次修改输入框的值,3 秒后的输出依然是点击前输入框的值。这就是所谓的 capture value 的特性。而在类组件中不会出现这种情况,在 3 秒后输出的就是修改后的值,因为这时候 message 是挂载在 this 变量上,它保留的是一个引用值,对 this 属性的访问都会获取到最新的值。此时我们可以用 useRef 来创建一个引用,就可以有效规避 React Hooks 中 Capture Value 特性。
function MessageThread() {
const latestMessage = useRef("");
const showMessage = () => {
alert("You said: " + latestMessage.current);
};
const handleSendClick = () => {
setTimeout(showMessage, 3000);
};
const handleMessageChange = e => {
latestMessage.current = e.target.value; // 这里就是赋值取值对象变为useRef
};
}
只要将赋值与取值的对象变成 useRef,而不是 useState,就可以躲过 capture value 特性,在 3 秒后得到最新的值。
补充: 当我们使用useRef来保存值时,一定要注意初始值设置里不能用函数来进行赋值,因为这样会造车每次render此函数都会执行。下面请看demo
const formatData = () => {
console.log('执行了');
return ‘初始值’
};
const testRef = useRef(formatData()); 每次 rerender 这个formData函数都会执行;为避免这种情况我们可以使用useEffect设置初始值; 或者直接使用useMemo
转载自:https://juejin.cn/post/7071998820225122335