React 钩子 useRef() 如何在幕后工作?那个参考到底是啥?
Posted
技术标签:
【中文标题】React 钩子 useRef() 如何在幕后工作?那个参考到底是啥?【英文标题】:How does React hook useRef() work under the hood? What is that reference exactly?React 钩子 useRef() 如何在幕后工作?那个参考到底是什么? 【发布时间】:2019-08-26 02:08:23 【问题描述】:我一直在处理 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 元素引用(只要它保持挂载,至少)。
From React Docs
这是因为 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 上,正如我们从上面的 sn-p 中看到的那样。
第 2 项
我可以说我在myDivElement.current
对象中得到的是HTMLElement(MDN 链接)类型之一吗?如果不是,它是什么类型/类型的对象?
第 3 项
myDivElement.current
正在初始化为 null
值。什么时候更改为div
的引用?是否在第一次渲染后发生?
额外编辑:
我做了这个额外的 sn-p 来显示一些在比较由ref
访问修改的 DOM 节点时的澄清 React 行为。
第一个按钮使用ref
直接修改DOM(切换红色和黑色),因此您会看到没有新渲染的颜色变化。
当您通过更改 state
强制蓝色并将其作为内联属性返回为渲染的返回时,您会立即看到它触发了重新渲染(因为它会更改蓝色状态)并且您会看到蓝色。
但奇怪的是,当您再次单击第一个按钮以通过ref
更改颜色时,它再次变为红色/黑色,但现在即使您强制更新也无法将其恢复为蓝色它。它将重新渲染但不更新 DOM。
由于蓝色的 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>
【问题讨论】:
【参考方案1】:引用
myDivElement.current
究竟指向什么
myDivElement.current
接收对底层 DOM 元素的引用
我可以说我在
myDivElement.current
对象中得到的东西是 输入 HTMLElement
当您将 ref 分配给 div 元素时,myDivElement.current
的类型将是 HTMLDivElement
myDivElement.current
正在初始化为空值。什么时候 对 div 的引用的更改? 1st之后会发生吗 渲染?
在第一次渲染期间,ref 对象在创建时被分配给 DOMNode。更新 DOM 节点也会导致对象发生变化
【讨论】:
" myDivElement.current 接收对底层 DOM 元素的引用" 你到底是什么意思?实际的 DOM ?就像绕过虚拟 DOM 一样吗?我不应该得到对 VDOM 的引用吗?如果我得到对 DOM 本身的引用,我正在更改它并且 React 正在根据该更改更新 VDOM?不应该反过来吗?比如,我们的代码会导致 VDOM 发生变化,而 React 会基于此更新 DOM?谢谢。 ref 包含对实际 DOM 而不是虚拟 DOM 的引用,您通过 ref 所做的更改也与 DOM 操作直接相关。此外,大多数时候您只需要从您使用 ref 的 DOM 访问某些属性。设置状态或更新样式应通过 css 类或通过 JSX 中的 style prop 完成 但这是否意味着 VDOM 将不同于 DOM 本身?就像我在 VDOM 中的div
节点不会有内联样式 color: 'red'
并且实际的 DOM 会有它? React 在下次渲染时如何处理这种解耦?不会被 VDOM 的内容代替吗?再次感谢!
VDOM 与 DOM 不同,是 DOM 的最小版本。并且 react 不要用 Virtual DOM 替换原始 dom 而只是进行更新更改以上是关于React 钩子 useRef() 如何在幕后工作?那个参考到底是啥?的主要内容,如果未能解决你的问题,请参考以下文章
如何在使用redux格式按下下一个键盘按钮后如何使用useRef钩子来选择下一个TextInput