`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);

只是为了强调一个目的:

createRefreturn {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.


1useRef 替换为 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