javascript - React 钩子 useRef() 如何在幕后工作?那个参考到底是什么?
问题描述
我一直在处理 React hook useRef() 的一些问题,我想这是因为我还没有真正掌握它的概念和功能。
我知道它可以用作函数范围之外的“全局”变量。所以下面的计数器工作得很好。我只需要强制更新它,因为更改myCounter.current
属性本身不会触发重新渲染。
const { useState, useRef } = React;
function App() {
const myCounter = useRef(0);
const [forceUpdate,setForceUpdate] = useState(true);
const handleClick = (e) => {
myCounter.current+=1;
setForceUpdate((prev)=>!prev);
}
return (
<div>
<div>{myCounter.current}</div>
<button onClick={handleClick}>Click</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
当我使用它来存储 HTML 元素的引用时,我的怀疑就开始了。从下面的 React 文档中,我们知道每次渲染都会得到相同的对象。所以我在每次渲染时都得到了相同的 html 元素引用(只要它保持挂载,至少)。
这是因为 useRef()创建了一个普通的 JavaScript 对象。useRef() 和自己创建 {current: ...} 对象之间的唯一区别是useRef 将在每次渲染时为您提供相同的 ref 对象。
例如:
const { useState, useRef } = React;
function App() {
const myDivElement = useRef(null);
const [forceUpdate,setForceUpdate] = useState(true);
const handleClick = (e) => {
if (myDivElement.current.style.color === 'red') {
myDivElement.current.style.color='black';
}
else {
myDivElement.current.style.color='red';
}
setForceUpdate((prev)=>!prev);
}
return (
<div>
<div ref={myDivElement}><b>Some content inside my Div element</b></div>
<button onClick={handleClick}>Click</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
注意:我知道在这里提出多个问题并不是最佳做法。但这更像是一种概念性的,理解以下项目将真正帮助我完全掌握这个钩子的功能。
项目 1
参考myDivElement.current
到底指向什么?它是否指向虚拟 DOM 中该元素的节点对象?因为我知道,例如,当我更改它的 CSS 属性时,我会看到该更改反映在 DOM 上,正如我们从上面的代码片段中看到的那样。
第 2 项
我可以说我在myDivElement.current
object 中得到的是HTMLElement (MDN link) 类型之一吗?如果不是,它是什么类型/类型的对象?
第 3 项
myDivElement.current
正在用null
值初始化。什么时候更改为 的引用div
?第一次渲染后会发生吗?
额外编辑:
ref
我制作了这个额外的片段,以显示在比较通过访问修改的 DOM 节点时的一些澄清 React 行为。
第一个按钮用于
ref
直接修改 DOM(切换红色和黑色),因此您会看到没有新渲染的颜色变化。当您通过更改强制蓝色
state
并将其作为渲染返回的内联属性返回时,您会立即看到它触发了重新渲染(因为它改变了蓝色状态)并且您看到了蓝色。但奇怪的是,当您再次单击第一个按钮以更改颜色时,
ref
它再次变为红色/黑色,但现在即使您强制更新也无法将其恢复为蓝色。它将重新渲染但不更新 DOM。由于 blue
state
已变为true
,react 将渲染结果与虚拟 DOM 中的结果进行比较(自从您第一次单击 Force Blue 后它一直是蓝色的)并作为相等的节点返回。它不知道 DOM 实际上是红色的,因为您已经直接通过ref
.
const { useState, useRef } = React;
function App() {
const myDivElement = useRef(null);
const [blue,setBlue] = useState(false);
const [forceUpdate,setForceUpdate] = useState(true);
const renderTimes = useRef(0);
renderTimes.current+=1;
const handleClick = (e) => {
if (myDivElement.current.style.color === 'red') {
myDivElement.current.style.color='black';
}
else {
myDivElement.current.style.color='red';
}
//setForceUpdate((prev)=>!prev);
//setBlue(false);
}
const handleClick2 = (e) => {
setForceUpdate((prev)=>!prev);
// setBlue(false);
}
const handleClick3 = (e) => {
setBlue(true);
}
const divStyle = {
color: 'blue',
};
return (
<div>
<div ref={myDivElement} style={{ color: blue? 'blue' : 'initial'}}><b>Some content inside my Div element</b></div>
<p>I was rendered {renderTimes.current} time(s)</p>
<button onClick={handleClick}>Toggle Color with useRef()</button>
<button onClick={handleClick2}>Force Update</button>
<button onClick={handleClick3}>Force Blue as inline style</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
解决方案
参考
myDivElement.current
到底指的是什么
myDivElement.current
接收对底层 DOM 元素的引用
我可以说我在
myDivElement.current
object 中得到的是 HTMLElement 类型之一
当您将 ref 分配给 div 元素时,myDivElement.current
它将是HTMLDivElement类型
myDivElement.current
正在使用空值初始化。什么时候改变 div 的引用?第一次渲染后会发生吗?
在第一次渲染期间,ref 对象在创建时被分配给 DOMNode。对 DOM 节点的更新也会导致对象发生变化
推荐阅读
- apache-spark - 将 to_date 列与 pyspark 中的单个值进行比较
- matplotlib - 轴(子图)和/或 matplotlib.pyplot(plt)上的条件语句
- flutter - 跨路线提供者,但在 MaterialApp 之后
- nginx - nginx rewrite:重写并传递原始 URI 参数
- sed - 如何将 sed 保留空间附加到以后的匹配中?
- java - 如果我用 Java 生成二进制文件,如何用 C++ 读取它?
- javascript - Vue,基于href中的链接关闭首先完成的方法
- sql - 检索非空列值
- javascript - 从通过 CacheStorage api 通过 cache.match(req) 返回的响应中检索“时间缓存”
- twilio - 如何构建 JSON 动作模式