“useRef”和“createRef”有什么区别?

2022-08-30 00:38:32

当我偶然发现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>
    </>
  );
}

...它似乎可以用.useRefcreateRef

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


答案 1

不同之处在于,这将始终创建新的引用。在基于类的组件中,您通常会在构造期间将 ref 放在实例属性中(例如 )。函数组件中没有此选项。 负责每次返回与初始渲染相同的引用。createRefthis.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);

编辑 1rvwnj71x3


答案 2

tldr

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

React.createRef()是一家工厂返回一个参考 - 不涉及魔法{ current: null }

useRef(initValue)还返回类似于 的引用。此外,它记住了这个引用在函数组件中的多个渲染中持久化。{ current: initValue }React.createRef()

在类组件中使用就足够了,因为 ref 对象被分配给一个实例变量,因此在整个组件及其生命周期中都可以访问:React.createRef

this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

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


1 替换为useRefuseState + createRef

以下推文对我一直很有启发性:

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

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

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

上面的代码“滥用”来持久保存从 返回的引用。 只是选择值部分 - 将是设置者。useStateReact.createRef()[0]useState[1]

useState导致与 相反的重新渲染。更正式地说,React 比较 的旧对象引用和新对象引用,当通过其 setter 方法设置新值时。如果我们改变直接的状态(与 setter 调用相反),它的行为或多或少地等同于 ,因为不再触发重新渲染:useRefuseStateuseStateuseRef

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

注意:不要这样做!使用优化的 useRef API,而不是重新发明轮子。以上仅供参考。