`useRef` 和 `createRef` 有什么区别?
What's the difference between `useRef` and `createRef`?
当我偶然发现 useRef
时,我正在浏览 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
存在?
createRef
总是 returns 一个新的引用,您通常将其作为字段存储在 class 组件的实例上。 useRef
returns 相同的 ref 在功能组件实例的每个渲染上。这就是允许 ref 的状态在渲染之间持续存在的原因,尽管您没有明确地将其存储在任何地方。
在您的第二个示例中,将在每次渲染时重新创建 ref。
不同之处在于 createRef
总是会创建一个新的 ref。在基于 class 的组件中,您通常会在构造期间将 ref 放在实例 属性 中(例如 this.input = createRef()
)。您在功能组件中没有此选项。 useRef
负责每次返回与初始渲染相同的引用。
下面是一个示例应用,展示了这两个函数的行为差异:
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);
只是为了强调一个目的:
createRef
和 return {current: null}
一样简单。这是一种以最现代的方式处理 ref=
prop 的方法,就是这样(虽然基于字符串的方式太神奇了,基于回调的方式看起来太冗长了)。
useRef
在渲染之前保留一些数据,更改它不会导致重新渲染(如 useState
那样)。他们很少相关。您对基于 class 的组件所期望的一切都转到实例字段 (this.* =
) 看起来像是要在功能组件中使用 useRef
实现的候选对象。
说 useCallback
作为有界 class 方法(this.handleClick = .....bind(this)
)工作并且可以用 [=13 重新实现(但我们肯定不应该重新发明轮子) =].
另一个示例是 DOM 参考、timeout/interval ID、任何第 3 方图书馆的标识符或参考。
PS 我相信 React 团队最好为 useRef
选择不同的命名以避免与 createRef
混淆。也许 useAndKeep
甚至 usePermanent
.
对其他人的答案的另一个重要补充。
您无法为 createRef
设置新值。但是你可以 useRef
.
const ur = useRef();
const cr = createRef();
ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it
tldr
一个ref
是一个普通的JS对象{ current: <some value> }
.
React.createRef()
是一个返回 ref { current: null }
- no magic involved.
的工厂
useRef(initValue)
也 returns 类似于 React.createRef()
的引用 { current: initValue }
。 除此之外,它memoizes这个引用在函数组件.
中的多个渲染中保持不变
在 class 组件中使用 React.createRef
就足够了,因为 ref 对象是 assigned to an instance variable,因此在整个组件及其生命周期中都可以访问:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)
useRef(null)
基本上is equivalent touseState(React.createRef())[0]
1.
1 将 useRef
替换为 useState
+ createRef
以下 tweet 对我有所启发:
useRef()
is basically useState({current: initialValue })[0]
.
根据 tldr
部分的见解,我们现在可以进一步得出结论:
useRef(null)
is basically useState(React.createRef())[0]
.
以上代码“滥用”useState
以保留从 React.createRef()
返回的引用。 [0]
仅选择 useState
的值部分 - [1]
将是 setter.
useState
导致重新渲染,与 useRef
不同。更正式地说,当通过其 setter 方法设置新值时,React 会比较 useState
的新旧对象引用。如果我们直接 改变 useState
的状态(与 setter 调用相反),它的行为或多或少会变成 等价的 至 useRef
,因为不再触发重新渲染:
// Example of mutating object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render
注意:不要这样做!使用优化的 useRef
API 而不是重新发明轮子。以上仅供参考。
ref 是一个普通的 JS 对象 { current: }。
React.useRef(initValue) return a ref { current: initValue }
it is remember ref value across multiple render of function component.
It is advise to use in Function component
React.createRef(initValue) also return a ref { current: initValue }
it is not remember ref value across multiple render of function components. It is advise to use in class based component
当我偶然发现 useRef
时,我正在浏览 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
存在?
createRef
总是 returns 一个新的引用,您通常将其作为字段存储在 class 组件的实例上。 useRef
returns 相同的 ref 在功能组件实例的每个渲染上。这就是允许 ref 的状态在渲染之间持续存在的原因,尽管您没有明确地将其存储在任何地方。
在您的第二个示例中,将在每次渲染时重新创建 ref。
不同之处在于 createRef
总是会创建一个新的 ref。在基于 class 的组件中,您通常会在构造期间将 ref 放在实例 属性 中(例如 this.input = createRef()
)。您在功能组件中没有此选项。 useRef
负责每次返回与初始渲染相同的引用。
下面是一个示例应用,展示了这两个函数的行为差异:
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);
只是为了强调一个目的:
createRef
和 return {current: null}
一样简单。这是一种以最现代的方式处理 ref=
prop 的方法,就是这样(虽然基于字符串的方式太神奇了,基于回调的方式看起来太冗长了)。
useRef
在渲染之前保留一些数据,更改它不会导致重新渲染(如 useState
那样)。他们很少相关。您对基于 class 的组件所期望的一切都转到实例字段 (this.* =
) 看起来像是要在功能组件中使用 useRef
实现的候选对象。
说 useCallback
作为有界 class 方法(this.handleClick = .....bind(this)
)工作并且可以用 [=13 重新实现(但我们肯定不应该重新发明轮子) =].
另一个示例是 DOM 参考、timeout/interval ID、任何第 3 方图书馆的标识符或参考。
PS 我相信 React 团队最好为 useRef
选择不同的命名以避免与 createRef
混淆。也许 useAndKeep
甚至 usePermanent
.
对其他人的答案的另一个重要补充。
您无法为 createRef
设置新值。但是你可以 useRef
.
const ur = useRef();
const cr = createRef();
ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it
tldr
一个ref
是一个普通的JS对象{ current: <some value> }
.
React.createRef()
是一个返回 ref { current: null }
- no magic involved.
useRef(initValue)
也 returns 类似于 React.createRef()
的引用 { current: initValue }
。 除此之外,它memoizes这个引用在函数组件.
在 class 组件中使用 React.createRef
就足够了,因为 ref 对象是 assigned to an instance variable,因此在整个组件及其生命周期中都可以访问:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)
useRef(null)
基本上is equivalent touseState(React.createRef())[0]
1.
1 将 useRef
替换为 useState
+ createRef
以下 tweet 对我有所启发:
useRef()
is basicallyuseState({current: initialValue })[0]
.
根据 tldr
部分的见解,我们现在可以进一步得出结论:
useRef(null)
is basicallyuseState(React.createRef())[0]
.
以上代码“滥用”useState
以保留从 React.createRef()
返回的引用。 [0]
仅选择 useState
的值部分 - [1]
将是 setter.
useState
导致重新渲染,与 useRef
不同。更正式地说,当通过其 setter 方法设置新值时,React 会比较 useState
的新旧对象引用。如果我们直接 改变 useState
的状态(与 setter 调用相反),它的行为或多或少会变成 等价的 至 useRef
,因为不再触发重新渲染:
// Example of mutating object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render
注意:不要这样做!使用优化的 useRef
API 而不是重新发明轮子。以上仅供参考。
ref 是一个普通的 JS 对象 { current: }。
React.useRef(initValue) return a ref { current: initValue }
it is remember ref value across multiple render of function component.
It is advise to use in Function component
React.createRef(initValue) also return a ref { current: initValue }
it is not remember ref value across multiple render of function components. It is advise to use in class based component