偶然发现MDN时,我正在浏览hooks文档。
看看他们的例子……
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
…似乎useRef
可以替换为createRef
。
function TextInputWithFocusButton() {
const inputRef = createRef(); // what's the diff?
const onButtonClick = () => {
// `current` points to the mounted text input element
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
为什么我需要一个挂钩来引用?为什么useRef
存在?
答案 0 :(得分:13)
区别在于createRef
将始终创建新的引用。在基于类的组件中,通常会在构造过程中将ref放在实例属性中(例如this.input = createRef()
)。您在功能组件中没有此选项。 useRef
负责每次返回与初始渲染相同的ref。
这是一个示例应用程序,演示了这两个功能的行为差异:
import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";
function App() {
const [renderIndex, setRenderIndex] = useState(1);
const refFromUseRef = useRef();
const refFromCreateRef = createRef();
if (!refFromUseRef.current) {
refFromUseRef.current = renderIndex;
}
if (!refFromCreateRef.current) {
refFromCreateRef.current = renderIndex;
}
return (
<div className="App">
Current render index: {renderIndex}
<br />
First render index remembered within refFromUseRef.current:
{refFromUseRef.current}
<br />
First render index unsuccessfully remembered within
refFromCreateRef.current:
{refFromCreateRef.current}
<br />
<button onClick={() => setRenderIndex(prev => prev + 1)}>
Cause re-render
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
答案 1 :(得分:9)
ref
是普通的JS对象{ current: <some value> }
。
React.createRef()
是返回引用{ current: null }
-no magic involved的工厂。
useRef(initValue)
还返回类似于{ current: initValue }
的引用React.createRef()
。 此外,它memoizes始终在功能组件中的多个渲染中保持不变。
React.createRef
就足够了,因为ref对象是assigned to an instance variable,因此在整个组件及其生命周期中都可以访问:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)
useRef(null)
基本上是is equivalent to useState(React.createRef())[0]
1 。
useRef
替换为useState
+ createRef
下面的tweet对我来说很有意义:
useRef()
基本上是useState({current: initialValue })[0]
。
借助tldr
部分的见解,我们现在可以进一步得出结论:
useRef(null)
基本上是useState(React.createRef())[0]
。
以上代码“滥用” useState
,以保留来自React.createRef()
的返回引用。 [0]
只需选择useState
的值部分-[1]
将是设置器。
useState
相比, useRef
导致重新渲染。更正式地说,当通过setter方法设置新值时,React比较useState
的旧对象引用和新对象引用。如果我们直接对useState
的状态进行变异(与setter调用相反),其行为或多或少地与useRef
等效 ,因为没有-render已被触发:
// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render
注意:请勿执行此操作!使用优化的useRef
API而不是重新发明轮子。以上是出于说明目的。
答案 2 :(得分:6)
createRef
总是返回一个新的引用,通常将其存储为类组件实例上的字段。 useRef
在每次渲染功能组件实例时均返回相同的引用。这是使ref的状态在渲染之间得以持久的原因,尽管您没有将其明确存储在任何地方。
在第二个示例中,将在每次渲染时重新创建引用。
答案 3 :(得分:1)
还有另一个但重要的补充。
您无法为let other = Decimal(35716) / Decimal(10) // 3571.6
let absoluteDistance = abs(position.totalCost.distance(to: other))
let accuracy = Decimal(1) / Decimal(100) // 0.01
XCTAssertTrue(absoluteDistance < accuracy)
设置新值。但是您可以为createRef
。
useRef
答案 4 :(得分:0)
仅突出目的:
createRef
和return {current: null}
一样简单。这是用最现代的方式处理ref=
道具的一种方法(仅基于字符串的方法太神奇了,而基于回调的魔术看起来太冗长了)。
useRef
在渲染之前保留一些数据,对其进行更改不会导致重新渲染(如useState
那样)。它们很少相关。对于基于类的组件,您期望的所有内容都将进入实例字段(this.* =
),看起来像是在功能组件中使用useRef
实现的候选对象。
说useCallback
作为有界类方法(this.handleClick = .....bind(this)
),可以用useRef
重新实现(但我们不应该肯定地重新发明轮子)。
另一个示例是DOM引用,超时/间隔ID,任何第三方库的标识符或引用。
PS,我相信React团队最好为useRef
选择不同的命名方式,以避免与createRef
混淆。也许是useAndKeep
甚至是usePermanent
。