React hook useRef() 是如何工作的?那个参考到底是什么?

How does React hook useRef() work under the hood? What is that reference exactly?

我一直在处理 React hook useRef() 的一些问题,我想这是因为我还没有真正掌握它的概念和功能。

我知道它可以用作函数范围之外的 'global' 变量。所以下面的计数器工作得很好。我只需要强制更新它,因为更改 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

This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.

例如:

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 对象中得到的是 HTMLElement (MDN link) 类型之一吗?如果不是,它是什么kind/type对象?

项目 3

myDivElement.current 正在用 null 值初始化。什么时候更改为 div 的引用?它会在第一次渲染后发生吗?

额外编辑:

我制作了这个额外的代码片段,以在比较 ref 访问修改的 DOM 节点时显示一些明确的 React 行为。

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>

What does the reference myDivElement.current points to exactly

myDivElement.current 接收对底层 DOM 元素的引用

Can I say that what I get in myDivElement.current object is one of the type HTMLElement

当您将 ref 分配给 div 元素时,myDivElement.current 的类型将为 HTMLDivElement

myDivElement.current is being initialized with null value. When does that change to the reference of the div? Does it happen after 1st render?

在第一次渲染期间,ref 对象在创建时被分配给 DOMNode。 DOM 节点的更新也会导致对象发生变化