【问题标题】: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>

【问题讨论】:

    标签: javascript html reactjs react-hooks


    【解决方案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 而只是进行更新更改
    猜你喜欢
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    • 2023-03-26
    • 2021-05-31
    • 2015-02-12
    • 2020-11-03
    相关资源
    最近更新 更多