React 如何更新 ref?

How does React update ref?

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [minus, setMinus] = useState(1);
  const ref = useRef(null);

  const handleClick = () => {
    setMinus(minus - 1);
  };

  console.log("-->", ref.current, minus);

  useEffect(() => {
    console.log(`denp[minus]>`, ref.current);
  }, [minus]);

  return (
    <div className="App">
      {(minus >= 0 || minus <= -5) && <h1 ref={ref}>num: {minus}</h1>}
      <button onClick={handleClick}>minus</button>
    </div>
  );
}

export default App;

在官方文档中,它说“只要节点发生变化,React 就会将其 .current 属性 设置为相应的 DOM 节点。”

所以在 React 渲染 DOM 之后,DOM 节点发生变化并且 React 更新 ref。然而,React 似乎并不是那样工作的。

在第一次渲染中,第一个console.log logs out ref为null

我按下按钮,状态减号更新为0

在第二个渲染中,第一个 console.log 注销 ref 是 <h1>num: 0</h1>。但是按照文档,我希望得到 <h1>num: 1</h1> 因为 ref 仅在 DOM 节点更新后更新

我按下按钮,状态减号更新为-1

在第三个渲染中,第一个 console.log 注销 ref: <h1>num: 0</h1>。现在它像文档一样工作。那么 React 是如何更新 ref 的呢?

此处为实时代码:https://codesandbox.io/s/quirky-architecture-r7wvr0

您被 console.log 工作方式的怪癖绊倒了。当您将对象传递给 console.log 时,浏览器会 而不是 立即将其转换为字符串,然后将其注销。相反,它会保存对该对象的引用,如果您稍后在控制台中检查该对象,它将在您单击控制台时评估该对象。如果对象在记录和单击之间发生了变化,您将看到新版本。

所以第一次渲染发生了,你 运行 console.log("-->", ref.current, minus);,结果为 null。然后你继续渲染过程,return 你想要的元素,并反应将它们放在 dom 上。现在元素在 dom 上,反应更新 ref.current.

然后进行第二次渲染,然后您注销 ref.current,其中包含第一次渲染的元素。此时此刻,内部文本为 1,但此时您不可能单击。渲染继续,您 return 您想要将其更改为 0,并做出反应,更新 dom。然后您进入控制台并单击日志。此时内部文本为0,所以这就是你看到的。

然后进行第三次渲染,然后您注销 ref.current。同样,它是第一个渲染的元素,由于最近的渲染,它的内部文本当前为 0。您 return 您想要卸载该元素,因此反应将其从 dom 中删除。现在渲染已经完成,反应更新 ref.current 为 null。您进入控制台并单击日志。该元素已从页面中删除,但它仍在内存中且未更改,因此您看到的内部文本为 0.


如果您想摆脱记录对象添加的复杂性,请尝试记录一个字符串,以便必须立即对其进行评估。例如:console.log("-->", ref.current?.innerHTML, minus);