useRef和createRef有什么区别?

时间:2019-02-10 20:27:16

标签: javascript reactjs react-hooks

偶然发现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存在?

5 个答案:

答案 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);

Edit 1rvwnj71x3

答案 1 :(得分:9)

tldr

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


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)

仅突出目的:

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

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

useCallback作为有界类方法(this.handleClick = .....bind(this)),可以用useRef重新实现(但我们不应该肯定地重新发明轮子)。

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

PS,我相信React团队最好为useRef选择不同的命名方式,以避免与createRef混淆。也许是useAndKeep甚至是usePermanent