useRef和createRef有什么区别?


120

我偶然发现钩子文档useRef

看他们的例子...

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存在?

Answers:


153

不同之处在于,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);

编辑1rvwnj71x3


38

createRef总是返回一个新的引用,通常将其存储为类组件实例上的字段。在每次渲染功能组件实例时都useRef返回相同的引用。这是使ref的状态在渲染之间得以持久的原因,尽管您没有将其明确存储在任何地方。

在您的第二个示例中,将在每次渲染时重新创建参考。


这是不正确的,您是否有引用来备份您的陈述?
Adeel Imran

1
React开发人员之一在这里发表评论,解释它是如何工作的:reddit.com/r/reactjs/comments/a2pt15/…我很想知道您对此答案有何误解
乔·克莱

在尝试回答此问题之前,我已经看到了该链接,它在您共享的链接中在何处说明了这一事实?我找不到吗?:)
Adeel Imran

1
我共享的链接显示了useRefReact开发人员之一发布的的简化实现。它不同于简单地调用createRefcreateRef它不是钩子,也不在调用之间保持任何状态。Ryan Cogswell的答案也很好地说明了这些差异。
乔·克莱

1
从该上下文中我的理解仅推断出useRef是在内部使用createRef的自定义钩子。感谢您分享知识。
Adeel Imran

13

tldr

Aref是一个普通的JS对象{ current: <some value> }

React.createRef()是一家退货的工厂{ current: null }-不涉及魔术

useRef(initValue)还返回{ current: initValue }类似于的引用React.createRef()此外,它还记住该引用在功能组件中的多个渲染器之间是持久的。

React.createRef在类组件中 使用就足够了,因为ref对象被分配给一个实例变量,因此在整个组件及其生命周期中都可以访问:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)基本上等于 useState(React.createRef())[0] 1


1替换useRefuseState+createRef

以下推文对我很有启发:

useRef()基本上是useState({current: initialValue })[0]

有了本tldr节的见解,我们现在可以进一步得出结论:

useRef(null)基本上是useState(React.createRef())[0]

上面的代码“滥用”useState以保留来自的返回引用React.createRef()[0]只是选择的价值部分useState-[1]将是二传手。

useState与相比,会导致重新渲染useRef。更正式地讲,useState当通过其setter方法设置新值时,React将比较旧对象引用和新对象引用。如果我们发生变异的状态useState直接(而非制定者调用),其行为或多或少变得等同useRef,因为没有重新渲染不再被触发:

// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render

注意:请勿这样做!使用优化的useRefAPI代替重新发明轮子。以上是出于说明目的。


3

只是为了强调一个目的:

createRef就像return {current: null}。这是一种以ref=最现代的方式处理道具的方法,就是这样(而基于字符串的方式太神奇了,基于魔术和基于回调的方式看起来太冗长了)。

useRef在渲染之前保留一些数据,对其进行更改不会导致重新渲染(useState确实如此)。它们很少相关。对于基于类的组件,您期望的所有内容都将进入实例fields(this.* =),看起来像是useRef在功能组件中实现的候选对象。

SayuseCallback可以作为有界类的method(this.handleClick = .....bind(this))来工作,并且可以用实现(但我们不应该肯定地重新发明轮子)useRef

另一个示例是DOM引用,超时/间隔ID,任何第三方库的标识符或引用。

PS我相信React团队最好选择不同的名字useRef来避免与混淆createRef。也许useAndKeep,甚至usePermanent


1

另一个答案的另一个但重要的补充。

您无法为设置新值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

ref是一个普通对象,您可以current照常更改其属性(刚刚测试过)。没关系,如果ref是通过useRef或创建的createRef
ford04年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.