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 的呢?
您被 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);
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 的呢?
您被 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);